164 lines
5.2 KiB
C#
164 lines
5.2 KiB
C#
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>();
|
|
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;
|
|
|
|
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)
|
|
{
|
|
CommandEnumerator 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, CommandEnumerator queue)
|
|
{
|
|
}
|
|
|
|
public virtual void Reset()
|
|
{
|
|
_zIndex = 0;
|
|
_zStack.Clear();
|
|
|
|
_viewport = new QRectangle(float.MaxValue, float.MinValue, float.MinValue, float.MaxValue);
|
|
_viewportStack.Clear();
|
|
|
|
_matrixStack.Clear();
|
|
_matrixStack.Push(QMat4.Identity);
|
|
}
|
|
|
|
private void ConditionalHandler(CommandEnumerator 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();
|
|
}
|
|
}
|
|
}
|
|
} |