using System; using System.Collections.Generic; namespace Quik.CommandMachine { public class CommandEngine { private int _zIndex = 0; private readonly Stack _zStack = new Stack(); public int ZIndex => _zIndex; private QRectangle _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 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().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>().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(); } } } }