GUIGraphTicks.cs 7.2 KB

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