//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
//**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************//
using System;
using BansheeEngine;
namespace BansheeEditor
{
/** @addtogroup AnimationEditor
* @{
*/
///
/// Renders a vertical value display that may be used as a side-bar for a graph display. User can set the range of the
/// values to display, as well as its physical dimensions.
///
internal class GUIGraphValues
{
private static readonly Color COLOR_TRANSPARENT_LIGHT_GRAY =
new Color(200.0f / 255.0f, 200.0f / 255.0f, 200.0f / 255.0f, 0.5f);
private GUIGraphTicks tickHandler;
private GUICanvas canvas;
private int width = 20;
private int height = 20;
private float rangeStart = -1.0f;
private float rangeEnd = 1.0f;
///
/// Constructs a new value display and adds it to the specified layout.
///
/// Layout to add the GUI element to.
/// Width of the timeline in pixels.
/// Height of the timeline in pixels.
public GUIGraphValues(GUILayout layout, int width, int height)
{
canvas = new GUICanvas();
layout.AddElement(canvas);
tickHandler = new GUIGraphTicks();
SetSize(width, height);
}
///
/// Sets the physical size onto which to draw the value display.
///
/// Width in pixels.
/// Height in pixels.
public void SetSize(int width, int height)
{
this.width = width;
this.height = height;
canvas.SetWidth(width);
canvas.SetHeight(height);
tickHandler.SetRange(rangeStart, rangeEnd, (uint)height);
}
///
/// Sets the range of values to display.
///
/// Minimum value to display.
/// Maximum value to display.
public void SetRange(float start, float end)
{
if (start > end)
{
float temp = start;
start = end;
end = temp;
}
rangeStart = start;
rangeEnd = end;
tickHandler.SetRange(rangeStart, rangeEnd, (uint)height);
}
///
/// Draws text displaying the value at the provided position.
///
/// Position to draw the text at.
/// Value to display.
/// If true the text will be displayed above the provided position, otherwise below.
private void DrawValue(int yPos, float value, bool above)
{
int exponent = MathEx.FloorToInt(MathEx.Log10(MathEx.Abs(value)));
int maxDecimalPoints = MathEx.Max(0, 1 - exponent);
string valueString = value.ToString("F" + maxDecimalPoints);
Vector2I textBounds = GUIUtility.CalculateTextBounds(valueString, EditorBuiltin.DefaultFont,
EditorStyles.DefaultFontSize);
Vector2I textPosition = new Vector2I();
textPosition.x = width - textBounds.x;
if (above)
textPosition.y = yPos - textBounds.y;
else // Below
{
const int PADDING = 3; // So the text doesn't touch the tick
textPosition.y = yPos + PADDING;
}
canvas.DrawText(valueString, textPosition, EditorBuiltin.DefaultFont, COLOR_TRANSPARENT_LIGHT_GRAY,
EditorStyles.DefaultFontSize);
}
///
/// Rebuilds the internal GUI elements. Should be called whenever timeline properties change.
///
public void Rebuild()
{
canvas.Clear();
int heightOffset = height/2;
float pixelsPerHeight;
if (rangeEnd != rangeStart)
pixelsPerHeight = height/(rangeEnd - rangeStart);
else
pixelsPerHeight = 0;
float yOffset = rangeStart + (rangeEnd - rangeStart)*0.5f;
int numTickLevels = (int)tickHandler.NumLevels;
for (int i = numTickLevels - 1; i >= 0; i--)
{
float[] ticks = tickHandler.GetTicks((uint)i);
float strength = tickHandler.GetLevelStrength((uint)i);
if (ticks.Length > 0)
{
for (int j = 0; j < ticks.Length; j++)
{
int yPos = (int) ((ticks[j] - yOffset) * pixelsPerHeight);
yPos = heightOffset - yPos; // Offset and flip height (canvas Y goes down)
Vector2I start = new Vector2I(0, yPos);
Vector2I end = new Vector2I((int) (width*strength), yPos);
Color color = COLOR_TRANSPARENT_LIGHT_GRAY;
color.a *= strength;
canvas.DrawLine(start, end, color);
// Draw text for the highest level ticks
if (i == 0)
DrawValue(yPos, ticks[j], ticks[j] <= 0.0f);
}
}
}
}
}
/** @} */
}