233 lines
8.0 KiB
C#
233 lines
8.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using OpenTK.Mathematics;
|
|
|
|
namespace Dashboard.ImmediateDraw
|
|
{
|
|
public class DrawingEngine
|
|
{
|
|
private int _zIndex = 0;
|
|
private readonly Stack<int> _zStack = new Stack<int>();
|
|
public int ZIndex => _zIndex;
|
|
|
|
private Rectangle _viewport;
|
|
private readonly Stack<Rectangle> _viewportStack = new Stack<Rectangle>();
|
|
private readonly Stack<Matrix4> _matrixStack = new Stack<Matrix4>();
|
|
|
|
private Command _customCommandBase = Command.CustomCommandBase;
|
|
private readonly List<CommandHandler> _customCommands = new List<CommandHandler>();
|
|
|
|
public Rectangle Viewport => _viewport;
|
|
|
|
public Matrix4 ActiveTransforms { get; }
|
|
|
|
public StyleStack Style { get; } = new StyleStack(new Style());
|
|
|
|
protected DrawingEngine()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
public Command RegisterCustomCommand(CommandHandler handler)
|
|
{
|
|
Command id = _customCommandBase++;
|
|
_customCommands.Insert(id - Command.CustomCommandBase, handler);
|
|
return id;
|
|
}
|
|
|
|
public void ProcessCommands(Rectangle bounds, DrawList queue)
|
|
{
|
|
DrawQueue iterator = queue.GetEnumerator();
|
|
|
|
if (!iterator.Peek().IsCommand)
|
|
throw new ArgumentException("The first element in the iterator must be a command frame.");
|
|
|
|
Reset();
|
|
|
|
_viewport = bounds;
|
|
_viewportStack.Push(_viewport);
|
|
|
|
Frame frame;
|
|
while (iterator.TryDequeue(out frame))
|
|
{
|
|
Command cmd = (Command)frame;
|
|
switch (cmd)
|
|
{
|
|
default:
|
|
if (cmd > Command.CustomCommandBase)
|
|
{
|
|
_customCommands[cmd - Command.CustomCommandBase].Invoke(this, iterator);
|
|
}
|
|
else
|
|
{
|
|
ChildProcessCommand(cmd, iterator);
|
|
}
|
|
break;
|
|
|
|
case Command.ConditionalBegin: ConditionalHandler(iterator); break;
|
|
case Command.ConditionalEnd: /* nop */ break;
|
|
|
|
case Command.Invoke:
|
|
iterator.Dequeue().As<CommandHandler>().Invoke(this, iterator);
|
|
break;
|
|
case Command.PushViewport:
|
|
_viewportStack.Push(_viewport);
|
|
break;
|
|
case Command.IntersectViewport:
|
|
_viewport = Rectangle.Intersect((Rectangle)iterator.Dequeue(), _viewport);
|
|
break;
|
|
case Command.StoreViewport:
|
|
_viewport = (Rectangle)iterator.Dequeue();
|
|
break;
|
|
case Command.PopViewport:
|
|
_viewport = _viewportStack.TryPop(out Rectangle viewport) ? viewport : bounds;
|
|
break;
|
|
case Command.PushZ:
|
|
_zStack.Push(_zIndex);
|
|
break;
|
|
case Command.IncrementZ:
|
|
_zIndex++;
|
|
break;
|
|
case Command.AddZ:
|
|
_zIndex += (int)iterator.Dequeue();
|
|
break;
|
|
case Command.StoreZ:
|
|
_zIndex = (int)iterator.Dequeue();
|
|
break;
|
|
case Command.DecrementZ:
|
|
_zIndex--;
|
|
break;
|
|
case Command.PopZ:
|
|
_zIndex = _zStack.TryPop(out int zindex) ? zindex : 0;
|
|
break;
|
|
case Command.PushStyle:
|
|
Style.Push(iterator.Dequeue().As<Style>());
|
|
break;
|
|
case Command.StoreStyle:
|
|
Style.Pop();
|
|
Style.Push(iterator.Dequeue().As<Style>());
|
|
break;
|
|
case Command.PopStyle:
|
|
Style.Pop();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void ChildProcessCommand(Command name, DrawQueue queue)
|
|
{
|
|
}
|
|
|
|
public virtual void Reset()
|
|
{
|
|
_zIndex = 0;
|
|
_zStack.Clear();
|
|
|
|
_viewport = new Rectangle(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue);
|
|
_viewportStack.Clear();
|
|
|
|
_matrixStack.Clear();
|
|
_matrixStack.Push(Matrix4.Identity);
|
|
}
|
|
|
|
private void ConditionalHandler(DrawQueue iterator)
|
|
{
|
|
Frame frame = iterator.Dequeue();
|
|
|
|
if (
|
|
frame.IsInteger && (int)frame != 0 ||
|
|
frame.As<Func<bool>>().Invoke())
|
|
{
|
|
// Take this branch.
|
|
return;
|
|
}
|
|
|
|
// Skip this branch.
|
|
int depth = 1;
|
|
while (iterator.TryPeek(out frame))
|
|
{
|
|
if (!frame.IsCommand)
|
|
{
|
|
iterator.Dequeue();
|
|
continue;
|
|
}
|
|
|
|
switch ((Command)frame)
|
|
{
|
|
case Command.ConditionalBegin:
|
|
// Increment conditional depth.
|
|
depth++;
|
|
break;
|
|
case Command.ConditionalEnd:
|
|
// Decrement condional depth, exit if zero.
|
|
if (--depth == 0)
|
|
{
|
|
iterator.Dequeue();
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
iterator.Dequeue();
|
|
}
|
|
}
|
|
|
|
private static readonly Dictionary<Type, IDrawListSerializer> s_serializers = new Dictionary<Type, IDrawListSerializer>();
|
|
|
|
/// <summary>
|
|
/// Add a custom serializer to the command engine.
|
|
/// </summary>
|
|
/// <typeparam name="T">Type object type.</typeparam>
|
|
/// <param name="serializer">The serializer.</param>
|
|
/// <param name="overwrite">True to allow overwriting.</param>
|
|
public static void AddSerializer<T>(IDrawListSerializer serializer, bool overwrite = false)
|
|
{
|
|
if (overwrite)
|
|
{
|
|
s_serializers[typeof(T)] = serializer;
|
|
}
|
|
else
|
|
{
|
|
s_serializers.Add(typeof(T), serializer);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a custom serializer to the command engine.
|
|
/// </summary>
|
|
/// <typeparam name="T">Type object type.</typeparam>
|
|
/// <param name="serializer">The serializer.</param>
|
|
/// <param name="overwrite">True to allow overwriting.</param>
|
|
public static void AddSerializer<T>(IDrawListSerializer<T> serializer, bool overwrite = false)
|
|
=> AddSerializer<T>((IDrawListSerializer)serializer, overwrite);
|
|
|
|
/// <summary>
|
|
/// Get a serializer for the given object.
|
|
/// </summary>
|
|
/// <typeparam name="T">The object type.</typeparam>
|
|
/// <param name="value">Required parameter for the C# type inference to work.</param>
|
|
/// <returns>The serializer.</returns>
|
|
public static IDrawListSerializer<T> GetSerializer<T>(IDrawListSerializable<T>? value)
|
|
where T : IDrawListSerializable<T>, new()
|
|
{
|
|
if (!s_serializers.TryGetValue(typeof(T), out var serializer))
|
|
{
|
|
serializer = new DrawListSerializableSerializer<T>();
|
|
AddSerializer<T>(serializer);
|
|
}
|
|
|
|
return (IDrawListSerializer<T>)serializer;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a serializer for the given object.
|
|
/// </summary>
|
|
/// <typeparam name="T">The object type.</typeparam>
|
|
/// <param name="value">Required parameter for the C# type inference to work.</param>
|
|
/// <returns>The serializer.</returns>
|
|
public static IDrawListSerializer<T> GetSerializer<T>(T? value)
|
|
{
|
|
return (IDrawListSerializer<T>)s_serializers[typeof(T)];
|
|
}
|
|
}
|
|
} |