GUIGraphTicks.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System;
  4. using System.Text;
  5. using BansheeEngine;
  6. namespace BansheeEditor
  7. {
  8. /** @addtogroup AnimationEditor
  9. * @{
  10. */
  11. /// <summary>
  12. /// Determines how should ticks reported by <see cref="GUIGraphTicks"/> be distributed.
  13. /// </summary>
  14. internal enum GUITickStepType
  15. {
  16. /// <summary>
  17. /// Ticks represent time values (Multiples of 60).
  18. /// </summary>
  19. Time,
  20. /// <summary>
  21. /// Ticks represent generic values (Multiples of 10).
  22. /// </summary>
  23. Generic
  24. }
  25. /// <summary>
  26. /// Generates a set of locations that can be used for rendering ticks on a graph. As input the class takes valid range,
  27. /// size of the area the ticks will be displayed on, type of ticks and minimum/maximum spacing and outputs a set of
  28. /// coordinates along which ticks should be positioned. Ticks are reported as multiple separate levels with different
  29. /// strengths, depending on how close their distribution is to the upper valid range.
  30. /// </summary>
  31. internal class GUIGraphTicks
  32. {
  33. private int pixelRange = 100;
  34. private float valueRangeStart = 0.0f;
  35. private float valueRangeEnd = 1.0f;
  36. private int minTickSpacingPx = 5;
  37. private int maxTickSpacingPx = 30;
  38. private float[] validSteps = new [] { 1.0f };
  39. private float[] levelStrengths = new[] { 1.0f };
  40. private int numLevels = 1;
  41. private int maxLevel = 0;
  42. public int NumLevels { get { return numLevels; } }
  43. /// <summary>
  44. /// Contructs a new tick generating object.
  45. /// </summary>
  46. /// <param name="stepType">Determines how will ticks be distributed.</param>
  47. internal GUIGraphTicks(GUITickStepType stepType = GUITickStepType.Generic)
  48. {
  49. if(stepType == GUITickStepType.Generic)
  50. SetGenericSteps();
  51. else
  52. SetTimeSteps();
  53. Rebuild();
  54. }
  55. /// <summary>
  56. /// Sets the range which ticks are to be displayed for, and the range along which the ticks will be displayed.
  57. /// </summary>
  58. /// <param name="valueRangeStart">Start of the range the ticks are to display.</param>
  59. /// <param name="valueRangeEnd">End of the range the ticks are to display.</param>
  60. /// <param name="pixelRange">Width or height on which the ticks will be rendered. In pixels.</param>
  61. internal void SetRange(float valueRangeStart, float valueRangeEnd, int pixelRange)
  62. {
  63. this.valueRangeStart = valueRangeStart;
  64. this.valueRangeEnd = valueRangeEnd;
  65. this.pixelRange = pixelRange;
  66. Rebuild();
  67. }
  68. /// <summary>
  69. /// Sets valid spacing between two ticks. Tick strength will be determined by how far away are they from either
  70. /// end of this range.
  71. /// </summary>
  72. /// <param name="minPx">Minimum spacing between two ticks, in pixels.</param>
  73. /// <param name="maxPx">Maximum spacing between two ticks, in pixels.</param>
  74. internal void SetTickSpacing(int minPx, int maxPx)
  75. {
  76. minTickSpacingPx = minPx;
  77. maxTickSpacingPx = maxPx;
  78. Rebuild();
  79. }
  80. /// <summary>
  81. /// Returns the strength of a particular tick level. Levels are ordered in descending order of strength (level 0 is
  82. /// the strongest).
  83. /// </summary>
  84. /// <param name="level">Level for which to retrieve the strength. Must not be larger than
  85. /// <see cref="NumLevels"/> - 1.</param>
  86. /// <returns>Strength of the ticks at this level, in range [0, 1].</returns>
  87. internal float GetLevelStrength(int level)
  88. {
  89. if (level < 0 || level >= numLevels)
  90. return 0.0f;
  91. return MathEx.Clamp01(levelStrengths[maxLevel + level]);
  92. }
  93. /// <summary>
  94. /// Returns positions of all ticks of the provided level. The ticks will be within the range provided to
  95. /// <see cref="SetRange"/>.
  96. /// </summary>
  97. /// <param name="level">Level for which to retrieve the positions. Must not be larger than
  98. /// <see cref="NumLevels"/> - 1.</param>
  99. /// <returns>Positions of all ticks of the provided level.</returns>
  100. internal float[] GetTicks(int level)
  101. {
  102. if (level < 0 || level >= numLevels)
  103. return new float[0];
  104. float step = validSteps[maxLevel + level];
  105. // Round up and down so we get one extra tick on either side (outside of value range)
  106. // (Useful when rendering text above the ticks, so the text doesn't just pop-in when the tick appears, instead
  107. // it is slowly clipped-in.)
  108. int startTick = MathEx.CeilToInt(valueRangeStart / step);
  109. int endTick = MathEx.FloorToInt(valueRangeEnd / step);
  110. int numTicks = endTick - startTick + 1;
  111. float[] ticks = new float[numTicks];
  112. for (int i = startTick; i <= endTick; i++)
  113. ticks[i - startTick] = i*step;
  114. return ticks;
  115. }
  116. /// <summary>
  117. /// Rebuilds the tick positions and strengths after some relevant parameter changes.
  118. /// </summary>
  119. private void Rebuild()
  120. {
  121. levelStrengths = new float[validSteps.Length];
  122. maxLevel = 0;
  123. float valueRange = valueRangeEnd - valueRangeStart;
  124. int tickSpacing = maxTickSpacingPx - minTickSpacingPx;
  125. int i = 0;
  126. for (; i < validSteps.Length; i++)
  127. {
  128. float tickSpacingPx = (validSteps[i]/valueRange) * pixelRange;
  129. levelStrengths[i] = (tickSpacingPx - minTickSpacingPx)/tickSpacing;
  130. if (levelStrengths[i] > 1.0f)
  131. maxLevel = i;
  132. else if (levelStrengths[i] < 0.0f)
  133. break;
  134. }
  135. if (i > 0)
  136. numLevels = i - maxLevel;
  137. else
  138. numLevels = 0;
  139. }
  140. /// <summary>
  141. /// Sets tick steps corresponding to time values. This will split the ticks into intervals relevant for displaying
  142. /// times.
  143. /// </summary>
  144. private void SetTimeSteps()
  145. {
  146. validSteps = new float[]
  147. {
  148. 3600.0f, 1800.0f, 600.0f, 300.0f,
  149. 60.0f, 30.0f, 10.0f, 5.0f,
  150. 1.0f, 0.5f, 0.25f, 0.1f, 0.05f, 0.01f
  151. };
  152. }
  153. /// <summary>
  154. /// Sets tick steps corresponding to generic values (as opposed to displaying time values).
  155. /// </summary>
  156. private void SetGenericSteps()
  157. {
  158. float minStep = 0.0000001f;
  159. int numSteps = 15;
  160. validSteps = new float[15 * 2];
  161. for (int i = numSteps - 1; i >= 0; i--)
  162. {
  163. validSteps[i * 2 + 1] = minStep;
  164. validSteps[i * 2 + 0] = minStep * 5;
  165. minStep *= 10.0f;
  166. }
  167. }
  168. }
  169. /** @} */
  170. }