using System; using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Dashboard.Drawing { public class DbBaseCommands : DrawExtension { public DrawCommand DrawPoint { get; } public DrawCommand DrawLine { get; } public RectCommand DrawRectF { get; } public RectCommand DrawRectS { get; } public RectCommand DrawRectFS { get; } private DbBaseCommands() : base("DB_base", new[] { BrushExtension.Instance, }) { AddCommand(DrawPoint = new DrawCommand("Point", this, PointCommandArgs.CommandSize)); AddCommand(DrawLine = new DrawCommand("Line", this, LineCommandArgs.CommandSize)); AddCommand(DrawRectF = new RectCommand(RectCommand.Mode.Fill)); AddCommand(DrawRectS = new RectCommand(RectCommand.Mode.Strike)); AddCommand(DrawRectFS = new RectCommand(RectCommand.Mode.FillStrike)); } public static readonly DbBaseCommands Instance = new DbBaseCommands(); } public struct PointCommandArgs : IParameterSerializer { public Vector3 Position { get; private set; } public float Size { get; private set; } public IBrush? Brush { get; private set; } public PointCommandArgs(Vector3 position, float size, IBrush brush) { Position = position; Brush = brush; Size = size; } public int Serialize(DrawQueue queue, Span bytes) { if (bytes.Length < CommandSize) return CommandSize; Span value = stackalloc Value[] { new Value(Position, Size, queue.RequireResource(Brush!)) }; MemoryMarshal.AsBytes(value).CopyTo(bytes); return CommandSize; } [MemberNotNull(nameof(Brush))] public void Deserialize(DrawQueue queue, ReadOnlySpan bytes) { if (bytes.Length < CommandSize) throw new Exception("Not enough bytes"); Value value = MemoryMarshal.AsRef(bytes); Position = value.Position; Size = value.Size; Brush = (IBrush)queue.Resources[value.BrushIndex]; } private record struct Value(Vector3 Position, float Size, int BrushIndex); public static readonly int CommandSize = Unsafe.SizeOf(); } public struct LineCommandArgs : IParameterSerializer { public Vector3 Start { get; private set; } public Vector3 End { get; private set; } public float Size { get; private set; } public IBrush? Brush { get; private set; } public LineCommandArgs(Vector3 start, Vector3 end, float size, IBrush brush) { Start = start; End = end; Brush = brush; Size = size; } public int Serialize(DrawQueue queue, Span bytes) { if (bytes.Length < CommandSize) return CommandSize; Span value = stackalloc Value[] { new Value(Start, End, Size, queue.RequireResource(Brush!)) }; MemoryMarshal.AsBytes(value).CopyTo(bytes); return CommandSize; } public void Deserialize(DrawQueue queue, ReadOnlySpan bytes) { if (bytes.Length < CommandSize) throw new Exception("Not enough bytes"); Value value = MemoryMarshal.AsRef(bytes); Start = value.Start; End = value.End; Size = value.Size; Brush = (IBrush)queue.Resources[value.BrushIndex]; } private record struct Value(Vector3 Start, Vector3 End, float Size, int BrushIndex); public static readonly int CommandSize = Unsafe.SizeOf(); } public enum StrikeKind { Inset = -1, Center = 0, Outset = 1, } public class RectCommand : IDrawCommand { private readonly Mode _mode; public string Name { get; } public IDrawExtension Extension { get; } public int Length { get; } public RectCommand(Mode mode) { Extension = DbBaseCommands.Instance; _mode = mode; switch (mode) { case Mode.Fill: Name = "RectF"; Length = Unsafe.SizeOf(); break; case Mode.Strike: Name = "RectS"; Length = Unsafe.SizeOf(); break; default: Name = "RectFS"; Length = Unsafe.SizeOf(); break; } } object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan param) { return GetParams(queue, param); } public RectCommandArgs GetParams(DrawQueue queue, ReadOnlySpan param) { if (param.Length < Length) throw new Exception("Not enough bytes"); RectCommandArgs args; switch (_mode) { case Mode.Fill: ref readonly RectF f = ref MemoryMarshal.AsRef(param); args = new RectCommandArgs(f.Start, f.End, (IBrush)queue.Resources[f.FillBrushIndex]); break; case Mode.Strike: ref readonly RectS s = ref MemoryMarshal.AsRef(param); args = new RectCommandArgs(s.Start, s.End, (IBrush)queue.Resources[s.StrikeBrushIndex], s.StrikeSize, s.StrikeKind); break; default: ref readonly RectFS fs = ref MemoryMarshal.AsRef(param); args = new RectCommandArgs(fs.Start, fs.End, (IBrush)queue.Resources[fs.FillBrushIndex], (IBrush)queue.Resources[fs.StrikeBrushIndex], fs.StrikeSize, fs.StrikeKind); break; } return args; } public int WriteParams(DrawQueue queue, object? obj, Span param) { return WriteParams(queue, (RectCommandArgs)obj, param); } public int WriteParams(DrawQueue queue, RectCommandArgs obj, Span param) { if (param.Length < Length) return Length; switch (_mode) { case Mode.Fill: ref RectF f = ref MemoryMarshal.AsRef(param); f.Start = obj.Start; f.End = obj.End; f.FillBrushIndex = queue.RequireResource(obj.FillBrush!); break; case Mode.Strike: ref RectS s = ref MemoryMarshal.AsRef(param); s.Start = obj.Start; s.End = obj.End; s.StrikeBrushIndex = queue.RequireResource(obj.StrikeBrush!); s.StrikeSize = obj.StrikeSize; s.StrikeKind = obj.StrikeKind; break; default: ref RectFS fs = ref MemoryMarshal.AsRef(param); fs.Start = obj.Start; fs.End = obj.End; fs.FillBrushIndex = queue.RequireResource(obj.FillBrush!); fs.StrikeBrushIndex = queue.RequireResource(obj.StrikeBrush!); fs.StrikeSize = obj.StrikeSize; fs.StrikeKind = obj.StrikeKind; break; } return Length; } [Flags] public enum Mode { Fill = 1, Strike = 2, FillStrike = Fill | Strike, } private record struct RectF(Vector3 Start, Vector3 End, int FillBrushIndex); private record struct RectS(Vector3 Start, Vector3 End, int StrikeBrushIndex, float StrikeSize, StrikeKind StrikeKind); private record struct RectFS(Vector3 Start, Vector3 End, int FillBrushIndex, int StrikeBrushIndex, float StrikeSize, StrikeKind StrikeKind); } public struct RectCommandArgs { public Vector3 Start { get; private set; } public Vector3 End { get; private set; } public StrikeKind StrikeKind { get; private set; } = StrikeKind.Center; public float StrikeSize { get; private set; } = 0f; public IBrush? FillBrush { get; private set; } = null; public IBrush? StrikeBrush { get; private set; } = null; public bool IsStruck => StrikeSize != 0; public RectCommandArgs(Vector3 start, Vector3 end, IBrush fillBrush) { Start = start; End = end; FillBrush = fillBrush; } public RectCommandArgs(Vector3 start, Vector3 end, IBrush strikeBrush, float strikeSize, StrikeKind strikeKind) { Start = start; End = end; StrikeBrush = strikeBrush; StrikeSize = strikeSize; StrikeKind = strikeKind; } public RectCommandArgs(Vector3 start, Vector3 end, IBrush fillBrush, IBrush strikeBrush, float strikeSize, StrikeKind strikeKind) { Start = start; End = end; FillBrush = fillBrush; StrikeBrush = strikeBrush; StrikeSize = strikeSize; StrikeKind = strikeKind; } } }