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(this, RectCommand.Mode.Fill)); AddCommand(DrawRectS = new RectCommand(this, RectCommand.Mode.Strike)); AddCommand(DrawRectFS = new RectCommand(this, RectCommand.Mode.FillStrike)); } public static readonly DbBaseCommands Instance = new DbBaseCommands(); } public struct PointCommandArgs : IParameterSerializer { public Vector2 Position { get; private set; } public float Depth { get; private set; } public float Size { get; private set; } public IBrush? Brush { get; private set; } public PointCommandArgs(Vector2 position, float depth, float size, IBrush brush) { Position = position; Depth = depth; Brush = brush; Size = size; } public int Serialize(DrawQueue queue, Span bytes) { if (bytes.Length < CommandSize) return CommandSize; Span value = stackalloc Value[] { new Value(Position, Depth, 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; Depth = value.Depth; Size = value.Size; Brush = (IBrush)queue.Resources[value.BrushIndex]; } private record struct Value(Vector2 Position, float Depth, float Size, int BrushIndex); public static readonly int CommandSize = Unsafe.SizeOf(); } public struct LineCommandArgs : IParameterSerializer { public Vector2 Start { get; private set; } public Vector2 End { get; private set; } public float Depth { get; private set; } public float Size { get; private set; } public IBrush? Brush { get; private set; } public LineCommandArgs(Vector2 start, Vector2 end, float depth, float size, IBrush brush) { Start = start; End = end; Depth = depth; Size = size; Brush = brush; } public int Serialize(DrawQueue queue, Span bytes) { if (bytes.Length < CommandSize) return CommandSize; Span value = stackalloc Value[] { new Value(Start, End, Depth, 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; Depth = value.Depth; Size = value.Size; Brush = (IBrush)queue.Resources[value.BrushIndex]; } private record struct Value(Vector2 Start, Vector2 End, float Depth, float Size, int BrushIndex); public static readonly int CommandSize = Unsafe.SizeOf(); } public class RectCommand : IDrawCommand { private readonly Mode _mode; public string Name { get; } public IDrawExtension Extension { get; } public int Length { get; } public RectCommand(IDrawExtension extension, Mode mode) { Extension = extension; _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, f.Depth, (IBrush)queue.Resources[f.FillBrushIndex]); break; case Mode.Strike: ref readonly RectS s = ref MemoryMarshal.AsRef(param); args = new RectCommandArgs(s.Start, s.End, s.Depth, (IBrush)queue.Resources[s.StrikeBrushIndex], s.StrikeSize, s.BorderKind); break; default: ref readonly RectFS fs = ref MemoryMarshal.AsRef(param); args = new RectCommandArgs(fs.Start, fs.End, fs.Depth, (IBrush)queue.Resources[fs.FillBrushIndex], (IBrush)queue.Resources[fs.StrikeBrushIndex], fs.StrikeSize, fs.BorderKind); 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.Depth = obj.Depth; 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.Depth = obj.Depth; s.StrikeBrushIndex = queue.RequireResource(obj.StrikeBrush!); s.StrikeSize = obj.StrikeSize; s.BorderKind = obj.BorderKind; break; default: ref RectFS fs = ref MemoryMarshal.AsRef(param); fs.Start = obj.Start; fs.End = obj.End; fs.Depth = obj.Depth; fs.FillBrushIndex = queue.RequireResource(obj.FillBrush!); fs.StrikeBrushIndex = queue.RequireResource(obj.StrikeBrush!); fs.StrikeSize = obj.StrikeSize; fs.BorderKind = obj.BorderKind; break; } return Length; } [Flags] public enum Mode { Fill = 1, Strike = 2, FillStrike = Fill | Strike, } private record struct RectF(Vector2 Start, Vector2 End, float Depth, int FillBrushIndex); private record struct RectS(Vector2 Start, Vector2 End, float Depth, int StrikeBrushIndex, float StrikeSize, BorderKind BorderKind); private record struct RectFS(Vector2 Start, Vector2 End, float Depth, int FillBrushIndex, int StrikeBrushIndex, float StrikeSize, BorderKind BorderKind); } public struct RectCommandArgs { public Vector2 Start { get; private set; } public Vector2 End { get; private set; } public float Depth { get; private set; } public float StrikeSize { get; private set; } = 0f; public BorderKind BorderKind { get; private set; } = BorderKind.Center; public IBrush? FillBrush { get; private set; } = null; public IBrush? StrikeBrush { get; private set; } = null; public bool IsStruck => StrikeSize != 0; public RectCommandArgs(Vector2 start, Vector2 end, float depth, IBrush fillBrush) { Start = start; End = end; Depth = depth; FillBrush = fillBrush; } public RectCommandArgs(Vector2 start, Vector2 end, float depth, IBrush strikeBrush, float strikeSize, BorderKind borderKind) { Start = start; End = end; Depth = depth; StrikeBrush = strikeBrush; StrikeSize = strikeSize; BorderKind = borderKind; } public RectCommandArgs(Vector2 start, Vector2 end, float depth, IBrush fillBrush, IBrush strikeBrush, float strikeSize, BorderKind borderKind) { Start = start; End = end; Depth = depth; FillBrush = fillBrush; StrikeBrush = strikeBrush; StrikeSize = strikeSize; BorderKind = borderKind; } } }