diff --git a/Quik/CommandQueue/CommandEngine.cs b/Quik/CommandQueue/CommandEngine.cs new file mode 100644 index 0000000..5f91ee9 --- /dev/null +++ b/Quik/CommandQueue/CommandEngine.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; + +namespace Quik.CommandQueue +{ + public class CommandEngine + { + private int _zIndex = 0; + private readonly Stack _zStack = new Stack(); + public int ZIndex => _zIndex; + + private QuikRectangle _viewport; + private readonly Stack _viewportStack = new Stack(); + private readonly Stack _matrixStack = new Stack(); + + private Command _customCommandBase = Command.CustomCommandBase; + private readonly List _customCommands = new List(); + + public QuikRectangle Viewport { get; } + + // TODO: Make a real matrix class. + public float[] ActiveTransforms { get; } + + public object ActiveStyle { get; } + + protected CommandEngine() + { + Reset(); + } + + public Command RegisterCustomCommand(QuikCommandHandler handler) + { + Command id = _customCommandBase++; + _customCommands.Insert(id - Command.CustomCommandBase, handler); + return id; + } + + public void ProcessCommands(QuikRectangle bounds, CommandQueue queue) + { + 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().Invoke(this, queue); + break; + case Command.PushViewport: + _viewportStack.Push(_viewport); + break; + case Command.IntersectViewport: + _viewport = QuikRectangle.Intersect((QuikRectangle)queue.Dequeue(), _viewport); + break; + case Command.StoreViewport: + _viewport = (QuikRectangle)queue.Dequeue(); + break; + case Command.PopViewport: + _viewport = _viewportStack.TryPop(out QuikRectangle viewport) ? viewport : bounds; + 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) + { + } + + private void Reset() + { + _zIndex = 0; + _zStack.Clear(); + + _viewport = new QuikRectangle(float.MaxValue, float.MaxValue, float.MinValue, float.MinValue); + _viewportStack.Clear(); + + _matrixStack.Clear(); + _matrixStack.Push(null); // TODO: replace with identity matrix. + } + + private void ConditionalHandler(CommandQueue queue) + { + Frame frame = queue.Dequeue(); + + if ( + frame.IsInteger && (int)frame != 0 || + frame.As>().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(); + } + } + } +} \ No newline at end of file diff --git a/Quik/CommandQueue/CommandHandler.cs b/Quik/CommandQueue/CommandHandler.cs index 01e2b67..50dc6f9 100644 --- a/Quik/CommandQueue/CommandHandler.cs +++ b/Quik/CommandQueue/CommandHandler.cs @@ -4,5 +4,5 @@ namespace Quik.CommandQueue /// A delegate for a QUIK command. /// /// The current stack. - public delegate void QuikCommandHandler(object state); + public delegate void QuikCommandHandler(CommandEngine state, CommandQueue queue); } \ No newline at end of file