//********************************** Banshee Engine (www.banshee3d.com) **************************************************// //**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************// using System; using bs; namespace bs.Editor { /** @addtogroup AnimationEditor * @{ */ /// /// Base class that can be implemented by objects needing to elements along draw a horizontal timeline. /// public class GUITimelineBase { public const int PADDING = 30; protected int drawableWidth; protected float rangeLength = 60.0f; protected float rangeOffset = 0.0f; protected GUICanvas canvas; protected int width; protected int height; protected int fps = 1; protected int markedFrameIdx = -1; /// /// Constructs a new timeline and adds it to the specified layout. /// /// Layout to add the timeline GUI to. /// Width of the timeline in pixels. /// Height of the timeline in pixels. public GUITimelineBase(GUILayout layout, int width, int height) { canvas = new GUICanvas(); layout.AddElement(canvas); SetSize(width, height); } /// /// Uses the assigned FPS, range and physical size to calculate the frame that is under the provided coordinates. /// /// Coordinate relative to the layout the GUI element is on. /// Frame that was clicked on, or -1 if the coordinates are outside of valid bounds. public int GetFrame(Vector2I pixelCoords) { Rect2I bounds = canvas.Bounds; if (pixelCoords.x < (bounds.x + PADDING) || pixelCoords.x >= (bounds.x + bounds.width - PADDING) || pixelCoords.y < bounds.y || pixelCoords.y >= (bounds.y + bounds.height)) { return -1; } Vector2I relativeCoords = pixelCoords - new Vector2I(bounds.x + PADDING, bounds.y); float lengthPerPixel = GetRange() / drawableWidth; float time = rangeOffset + relativeCoords.x * lengthPerPixel; return MathEx.RoundToInt(time * fps); } /// /// Returns the time at the specified pixel value along the timeline. /// /// Coordinate relative to this GUI element, in pixels. /// Time along the curve at the specified coordinates. public float GetTime(int pixel) { Rect2I bounds = canvas.Bounds; int relativeCoords = pixel - (bounds.x + PADDING); float lengthPerPixel = GetRange() / drawableWidth; return rangeOffset + relativeCoords * lengthPerPixel; } /// /// Finds the pixel offset relative to the GUI element's origin, of the specified time. /// /// Time value to return the offset for. /// Offset in pixels relative to GUI element's origin. public int GetOffset(float time) { return (int)(((time - rangeOffset) / GetRange()) * drawableWidth) + PADDING; } /// /// Returns time for a frame with the specified index. Depends on set range and FPS. /// /// Index of the frame (not a key-frame) to get the time for. /// Time of the frame with the provided index. public float GetTimeForFrame(int frameIdx) { return frameIdx / (float)fps; } /// /// Sets the frame at which to display the frame marker. /// /// Index of the frame to display the marker on, or -1 to clear the marker. public void SetMarkedFrame(int frameIdx) { markedFrameIdx = frameIdx; } /// /// Sets the physical size onto which to draw the timeline. /// /// 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); drawableWidth = Math.Max(0, width - PADDING * 2); } /// /// Sets the range of values to display on the timeline. /// /// Amount of time to display, in seconds. public void SetRange(float length) { rangeLength = Math.Max(0.0f, length); } /// /// Returns the offset at which the displayed timeline values start at. /// /// Value to start the timeline values at, in seconds. public void SetOffset(float offset) { rangeOffset = offset; } /// /// Number of frames per second, used for frame selection and marking. /// /// Number of prames per second. public void SetFPS(int fps) { this.fps = Math.Max(1, fps); } /// /// Returns the range of times displayed by the timeline rounded to the multiple of FPS. /// /// If true, extra range will be included to cover the right-most padding. /// Time range rounded to a multiple of FPS. protected float GetRange(bool padding = false) { float spf = 1.0f / fps; float range = rangeLength; if (padding) { float lengthPerPixel = rangeLength / drawableWidth; range += lengthPerPixel * PADDING; } return MathEx.Max(1.0f, range / spf) * spf; } /// /// Draws a vertical frame marker at the specified time. /// /// Time at which to draw the marker. private void DrawFrameMarker(float t) { int xPos = (int)(((t - rangeOffset) / GetRange()) * drawableWidth) + PADDING; Vector2I start = new Vector2I(xPos, 0); Vector2I end = new Vector2I(xPos, height); canvas.DrawLine(start, end, Color.BansheeOrange); } /// /// Draws the frame marker at the currently selected frame. /// protected void DrawFrameMarker() { if (markedFrameIdx != -1) DrawFrameMarker(markedFrameIdx / (float)fps); } /// /// Rebuilds the internal GUI elements. Should be called whenever timeline properties change. /// public virtual void Rebuild() { canvas.Clear(); } } /** @} */ }