//********************************** 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); } } } } } /** @} */ }