Dashboard/Quik/CommandMachine/CommandEngine.cs

164 lines
5.2 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
namespace Quik.CommandMachine
{
public class CommandEngine
{
private int _zIndex = 0;
private readonly Stack<int> _zStack = new Stack<int>();
public int ZIndex => _zIndex;
private QRectangle _viewport;
private readonly Stack<QRectangle> _viewportStack = new Stack<QRectangle>();
2023-06-30 18:57:04 +02:00
private readonly Stack<QMat4> _matrixStack = new Stack<QMat4>();
private Command _customCommandBase = Command.CustomCommandBase;
private readonly List<QuikCommandHandler> _customCommands = new List<QuikCommandHandler>();
public QRectangle Viewport => _viewport;
2023-06-30 18:57:04 +02:00
public QMat4 ActiveTransforms { get; }
public StyleStack Style { get; } = new StyleStack(new Quik.Style());
protected CommandEngine()
{
Reset();
}
public Command RegisterCustomCommand(QuikCommandHandler handler)
{
Command id = _customCommandBase++;
_customCommands.Insert(id - Command.CustomCommandBase, handler);
return id;
}
public void ProcessCommands(QRectangle bounds, CommandList queue)
{
CommandQueue 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<QuikCommandHandler>().Invoke(this, iterator);
break;
case Command.PushViewport:
_viewportStack.Push(_viewport);
break;
case Command.IntersectViewport:
_viewport = QRectangle.Intersect((QRectangle)iterator.Dequeue(), _viewport);
break;
case Command.StoreViewport:
_viewport = (QRectangle)iterator.Dequeue();
break;
case Command.PopViewport:
_viewport = _viewportStack.TryPop(out QRectangle 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;
}
}
}
protected virtual void ChildProcessCommand(Command name, CommandQueue queue)
{
}
public virtual void Reset()
{
_zIndex = 0;
_zStack.Clear();
_viewport = new QRectangle(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue);
_viewportStack.Clear();
_matrixStack.Clear();
2023-06-30 18:57:04 +02:00
_matrixStack.Push(QMat4.Identity);
}
private void ConditionalHandler(CommandQueue 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();
}
}
}
}