2023-05-15 09:22:09 +02:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
2023-05-20 18:37:51 +02:00
|
|
|
namespace Quik.CommandMachine
|
2023-05-15 09:22:09 +02:00
|
|
|
{
|
|
|
|
public class CommandEngine
|
|
|
|
{
|
|
|
|
private int _zIndex = 0;
|
|
|
|
private readonly Stack<int> _zStack = new Stack<int>();
|
|
|
|
public int ZIndex => _zIndex;
|
|
|
|
|
2023-06-29 09:42:02 +02:00
|
|
|
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>();
|
2023-05-15 09:22:09 +02:00
|
|
|
|
|
|
|
private Command _customCommandBase = Command.CustomCommandBase;
|
|
|
|
private readonly List<QuikCommandHandler> _customCommands = new List<QuikCommandHandler>();
|
|
|
|
|
2023-06-29 09:42:02 +02:00
|
|
|
public QRectangle Viewport => _viewport;
|
2023-05-15 09:22:09 +02:00
|
|
|
|
2023-06-30 18:57:04 +02:00
|
|
|
public QMat4 ActiveTransforms { get; }
|
2023-05-15 09:22:09 +02:00
|
|
|
|
2023-06-29 09:31:28 +02:00
|
|
|
public StyleStack Style { get; } = new StyleStack(new Quik.Style());
|
2023-05-15 09:22:09 +02:00
|
|
|
|
|
|
|
protected CommandEngine()
|
|
|
|
{
|
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
public Command RegisterCustomCommand(QuikCommandHandler handler)
|
|
|
|
{
|
|
|
|
Command id = _customCommandBase++;
|
|
|
|
_customCommands.Insert(id - Command.CustomCommandBase, handler);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2023-06-29 09:42:02 +02:00
|
|
|
public void ProcessCommands(QRectangle bounds, CommandQueue queue)
|
2023-05-15 09:22:09 +02:00
|
|
|
{
|
|
|
|
if (!queue.Peek().IsCommand)
|
|
|
|
throw new ArgumentException("The first element in the queue must be a command frame.");
|
|
|
|
|
|
|
|
Reset();
|
|
|
|
|
|
|
|
_viewport = bounds;
|
|
|
|
_viewportStack.Push(_viewport);
|
|
|
|
|
|
|
|
Frame frame;
|
|
|
|
while (queue.TryDequeue(out frame))
|
|
|
|
{
|
|
|
|
Command cmd = (Command)frame;
|
|
|
|
switch (cmd)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
if (cmd > Command.CustomCommandBase)
|
|
|
|
{
|
|
|
|
_customCommands[cmd - Command.CustomCommandBase].Invoke(this, queue);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ChildProcessCommand(cmd, queue);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Command.ConditionalBegin: ConditionalHandler(queue); break;
|
|
|
|
case Command.ConditionalEnd: /* nop */ break;
|
|
|
|
|
|
|
|
case Command.Invoke:
|
|
|
|
queue.Dequeue().As<QuikCommandHandler>().Invoke(this, queue);
|
|
|
|
break;
|
|
|
|
case Command.PushViewport:
|
|
|
|
_viewportStack.Push(_viewport);
|
|
|
|
break;
|
|
|
|
case Command.IntersectViewport:
|
2023-06-29 09:42:02 +02:00
|
|
|
_viewport = QRectangle.Intersect((QRectangle)queue.Dequeue(), _viewport);
|
2023-05-15 09:22:09 +02:00
|
|
|
break;
|
|
|
|
case Command.StoreViewport:
|
2023-06-29 09:42:02 +02:00
|
|
|
_viewport = (QRectangle)queue.Dequeue();
|
2023-05-15 09:22:09 +02:00
|
|
|
break;
|
|
|
|
case Command.PopViewport:
|
2023-06-29 09:42:02 +02:00
|
|
|
_viewport = _viewportStack.TryPop(out QRectangle viewport) ? viewport : bounds;
|
2023-05-15 09:22:09 +02:00
|
|
|
break;
|
|
|
|
case Command.PushZ:
|
|
|
|
_zStack.Push(_zIndex);
|
|
|
|
break;
|
|
|
|
case Command.IncrementZ:
|
|
|
|
_zIndex++;
|
|
|
|
break;
|
|
|
|
case Command.AddZ:
|
|
|
|
_zIndex += (int)queue.Dequeue();
|
|
|
|
break;
|
|
|
|
case Command.StoreZ:
|
|
|
|
_zIndex = (int)queue.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)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-06-29 09:31:28 +02:00
|
|
|
public virtual void Reset()
|
2023-05-15 09:22:09 +02:00
|
|
|
{
|
|
|
|
_zIndex = 0;
|
|
|
|
_zStack.Clear();
|
|
|
|
|
2023-06-29 09:42:02 +02:00
|
|
|
_viewport = new QRectangle(float.MaxValue, float.MaxValue, float.MinValue, float.MinValue);
|
2023-05-15 09:22:09 +02:00
|
|
|
_viewportStack.Clear();
|
|
|
|
|
|
|
|
_matrixStack.Clear();
|
2023-06-30 18:57:04 +02:00
|
|
|
_matrixStack.Push(QMat4.Identity);
|
2023-05-15 09:22:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private void ConditionalHandler(CommandQueue queue)
|
|
|
|
{
|
|
|
|
Frame frame = queue.Dequeue();
|
|
|
|
|
|
|
|
if (
|
|
|
|
frame.IsInteger && (int)frame != 0 ||
|
|
|
|
frame.As<Func<bool>>().Invoke())
|
|
|
|
{
|
|
|
|
// Take this branch.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip this branch.
|
|
|
|
int depth = 1;
|
|
|
|
while (queue.TryPeek(out frame))
|
|
|
|
{
|
|
|
|
if (!frame.IsCommand)
|
|
|
|
{
|
|
|
|
queue.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)
|
|
|
|
{
|
|
|
|
queue.Dequeue();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
queue.Dequeue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|