//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
//**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************//
using System;
using System.Text;
using BansheeEngine;
namespace BansheeEditor
{
/** @addtogroup AnimationEditor
* @{
*/
///
/// Determines how should ticks reported by be distributed.
///
internal enum GUITickStepType
{
///
/// Ticks represent time values (Multiples of 60).
///
Time,
///
/// Ticks represent generic values (Multiples of 10).
///
Generic
}
///
/// Generates a set of locations that can be used for rendering ticks on a graph. As input the class takes valid range,
/// size of the area the ticks will be displayed on, type of ticks and minimum/maximum spacing and outputs a set of
/// coordinates along which ticks should be positioned. Ticks are reported as multiple separate levels with different
/// strengths, depending on how close their distribution is to the upper valid range.
///
internal class GUIGraphTicks
{
private int pixelRange = 100;
private float valueRangeStart = 0.0f;
private float valueRangeEnd = 1.0f;
private int minTickSpacingPx = 5;
private int maxTickSpacingPx = 30;
private float[] validSteps = new [] { 1.0f };
private float[] levelStrengths = new[] { 1.0f };
private int numLevels = 1;
private int maxLevel = 0;
public int NumLevels { get { return numLevels; } }
///
/// Contructs a new tick generating object.
///
/// Determines how will ticks be distributed.
internal GUIGraphTicks(GUITickStepType stepType = GUITickStepType.Generic)
{
if(stepType == GUITickStepType.Generic)
SetGenericSteps();
else
SetTimeSteps();
Rebuild();
}
///
/// Sets the range which ticks are to be displayed for, and the range along which the ticks will be displayed.
///
/// Start of the range the ticks are to display.
/// End of the range the ticks are to display.
/// Width or height on which the ticks will be rendered. In pixels.
internal void SetRange(float valueRangeStart, float valueRangeEnd, int pixelRange)
{
this.valueRangeStart = valueRangeStart;
this.valueRangeEnd = valueRangeEnd;
this.pixelRange = pixelRange;
Rebuild();
}
///
/// Sets valid spacing between two ticks. Tick strength will be determined by how far away are they from either
/// end of this range.
///
/// Minimum spacing between two ticks, in pixels.
/// Maximum spacing between two ticks, in pixels.
internal void SetTickSpacing(int minPx, int maxPx)
{
minTickSpacingPx = minPx;
maxTickSpacingPx = maxPx;
Rebuild();
}
///
/// Returns the strength of a particular tick level. Levels are ordered in descending order of strength (level 0 is
/// the strongest).
///
/// Level for which to retrieve the strength. Must not be larger than
/// - 1.
/// Strength of the ticks at this level, in range [0, 1].
internal float GetLevelStrength(int level)
{
if (level < 0 || level >= numLevels)
return 0.0f;
return MathEx.Clamp01(levelStrengths[maxLevel + level]);
}
///
/// Returns positions of all ticks of the provided level. The ticks will be within the range provided to
/// .
///
/// Level for which to retrieve the positions. Must not be larger than
/// - 1.
/// Positions of all ticks of the provided level.
internal float[] GetTicks(int level)
{
if (level < 0 || level >= numLevels)
return new float[0];
float step = validSteps[maxLevel + level];
// Round up and down so we get one extra tick on either side (outside of value range)
// (Useful when rendering text above the ticks, so the text doesn't just pop-in when the tick appears, instead
// it is slowly clipped-in.)
int startTick = MathEx.CeilToInt(valueRangeStart / step);
int endTick = MathEx.FloorToInt(valueRangeEnd / step);
int numTicks = endTick - startTick + 1;
float[] ticks = new float[numTicks];
for (int i = startTick; i <= endTick; i++)
ticks[i - startTick] = i*step;
return ticks;
}
///
/// Rebuilds the tick positions and strengths after some relevant parameter changes.
///
private void Rebuild()
{
levelStrengths = new float[validSteps.Length];
maxLevel = 0;
float valueRange = valueRangeEnd - valueRangeStart;
int tickSpacing = maxTickSpacingPx - minTickSpacingPx;
int i = 0;
for (; i < validSteps.Length; i++)
{
float tickSpacingPx = (validSteps[i]/valueRange) * pixelRange;
levelStrengths[i] = (tickSpacingPx - minTickSpacingPx)/tickSpacing;
if (levelStrengths[i] > 1.0f)
maxLevel = i;
else if (levelStrengths[i] < 0.0f)
break;
}
if (i > 0)
numLevels = i - maxLevel;
else
numLevels = 0;
}
///
/// Sets tick steps corresponding to time values. This will split the ticks into intervals relevant for displaying
/// times.
///
private void SetTimeSteps()
{
validSteps = new float[]
{
3600.0f, 1800.0f, 600.0f, 300.0f,
60.0f, 30.0f, 10.0f, 5.0f,
1.0f, 0.5f, 0.25f, 0.1f, 0.05f, 0.01f
};
}
///
/// Sets tick steps corresponding to generic values (as opposed to displaying time values).
///
private void SetGenericSteps()
{
float minStep = 0.0000001f;
int numSteps = 15;
validSteps = new float[15 * 2];
for (int i = numSteps - 1; i >= 0; i--)
{
validSteps[i * 2 + 1] = minStep;
validSteps[i * 2 + 0] = minStep * 5;
minStep *= 10.0f;
}
}
}
/** @} */
}