Compare commits

...

7 Commits

12 changed files with 541 additions and 57 deletions

31
Dashboard.Common/Box3d.cs Normal file

@ -0,0 +1,31 @@
using System.Numerics;
namespace Dashboard
{
public readonly record struct Box3d(Vector3 Min, Vector3 Max)
{
public float Left => Min.X;
public float Right => Max.X;
public float Top => Min.Y;
public float Bottom => Max.Y;
public float Far => Min.Z;
public float Near => Max.Z;
public Vector3 Size => Max - Min;
public Vector3 Center => Min + Size / 2f;
public static Box3d Union(Box3d left, Box3d right)
{
Vector3 min = Vector3.Min(left.Min, right.Min);
Vector3 max = Vector3.Max(left.Max, right.Max);
return new Box3d(min, max);
}
public static Box3d Intersect(Box3d left, Box3d right)
{
Vector3 min = Vector3.Max(left.Min, right.Min);
Vector3 max = Vector3.Min(left.Max, right.Max);
return new Box3d(min, max);
}
}
}

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

@ -0,0 +1,38 @@
using System.Collections;
using System.Collections.Generic;
namespace Dashboard.Drawing
{
public class HashList<T> : IReadOnlyList<T>
where T : notnull
{
private readonly List<T> _list = new List<T>();
private readonly Dictionary<T, int> _map = new Dictionary<T, int>();
public T this[int index] => _list[index];
public int Count => _list.Count;
public int Intern(T value)
{
if (_map.TryGetValue(value, out int index))
return index;
index = Count;
_list.Add(value);
_map.Add(value, index);
return index;
}
public void Clear()
{
_list.Clear();
_map.Clear();
}
public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator();
}
}

@ -6,4 +6,8 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Dashboard.Common\Dashboard.Common.csproj" />
</ItemGroup>
</Project> </Project>

@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -9,6 +10,10 @@ namespace Dashboard.Drawing
public class DbBaseCommands : DrawExtension public class DbBaseCommands : DrawExtension
{ {
public DrawCommand<PointCommandArgs> DrawPoint { get; } public DrawCommand<PointCommandArgs> DrawPoint { get; }
public DrawCommand<LineCommandArgs> DrawLine { get; }
public RectCommand DrawRectF { get; }
public RectCommand DrawRectS { get; }
public RectCommand DrawRectFS { get; }
private DbBaseCommands() : base("DB_base", private DbBaseCommands() : base("DB_base",
new[] new[]
@ -17,6 +22,10 @@ namespace Dashboard.Drawing
}) })
{ {
AddCommand(DrawPoint = new DrawCommand<PointCommandArgs>("Point", this, PointCommandArgs.CommandSize)); AddCommand(DrawPoint = new DrawCommand<PointCommandArgs>("Point", this, PointCommandArgs.CommandSize));
AddCommand(DrawLine = new DrawCommand<LineCommandArgs>("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 static readonly DbBaseCommands Instance = new DbBaseCommands();
@ -34,7 +43,7 @@ namespace Dashboard.Drawing
Brush = brush; Brush = brush;
Size = size; Size = size;
} }
public int Serialize(DrawQueue queue, Span<byte> bytes) public int Serialize(DrawQueue queue, Span<byte> bytes)
{ {
if (bytes.Length < CommandSize) if (bytes.Length < CommandSize)
@ -66,4 +75,210 @@ namespace Dashboard.Drawing
public static readonly int CommandSize = Unsafe.SizeOf<Value>(); public static readonly int CommandSize = Unsafe.SizeOf<Value>();
} }
public struct LineCommandArgs : IParameterSerializer<LineCommandArgs>
{
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<byte> bytes)
{
if (bytes.Length < CommandSize)
return CommandSize;
Span<Value> value = stackalloc Value[]
{
new Value(Start, End, Size, queue.RequireResource(Brush!))
};
MemoryMarshal.AsBytes(value).CopyTo(bytes);
return CommandSize;
}
public void Deserialize(DrawQueue queue, ReadOnlySpan<byte> bytes)
{
if (bytes.Length < CommandSize)
throw new Exception("Not enough bytes");
Value value = MemoryMarshal.AsRef<Value>(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<Value>();
}
public enum StrikeKind
{
Inset = -1,
Center = 0,
Outset = 1,
}
public class RectCommand : IDrawCommand<RectCommandArgs>
{
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<RectF>();
break;
case Mode.Strike:
Name = "RectS";
Length = Unsafe.SizeOf<RectS>();
break;
default:
Name = "RectFS";
Length = Unsafe.SizeOf<RectFS>();
break;
}
}
object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
{
return GetParams(queue, param);
}
public RectCommandArgs GetParams(DrawQueue queue, ReadOnlySpan<byte> 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<RectF>(param);
args = new RectCommandArgs(f.Start, f.End, (IBrush)queue.Resources[f.FillBrushIndex]);
break;
case Mode.Strike:
ref readonly RectS s = ref MemoryMarshal.AsRef<RectS>(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<RectFS>(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<byte> param)
{
return WriteParams(queue, (RectCommandArgs)obj, param);
}
public int WriteParams(DrawQueue queue, RectCommandArgs obj, Span<byte> param)
{
if (param.Length < Length)
return Length;
switch (_mode)
{
case Mode.Fill:
ref RectF f = ref MemoryMarshal.AsRef<RectF>(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<RectS>(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<RectFS>(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;
}
}
} }

@ -29,6 +29,8 @@ namespace Dashboard.Drawing
/// <param name="param">The parameter array.</param> /// <param name="param">The parameter array.</param>
/// <returns>The parameters object.</returns> /// <returns>The parameters object.</returns>
object? GetParams(DrawQueue queue, ReadOnlySpan<byte> param); object? GetParams(DrawQueue queue, ReadOnlySpan<byte> param);
int WriteParams(DrawQueue queue, object? obj, Span<byte> param);
} }
public interface IDrawCommand<T> : IDrawCommand public interface IDrawCommand<T> : IDrawCommand
@ -39,6 +41,8 @@ namespace Dashboard.Drawing
/// <param name="param">The parameter array.</param> /// <param name="param">The parameter array.</param>
/// <returns>The parameters object.</returns> /// <returns>The parameters object.</returns>
new T? GetParams(DrawQueue queue, ReadOnlySpan<byte> param); new T? GetParams(DrawQueue queue, ReadOnlySpan<byte> param);
new int WriteParams(DrawQueue queue, T? obj, Span<byte> param);
} }
public sealed class DrawCommand : IDrawCommand public sealed class DrawCommand : IDrawCommand
@ -57,6 +61,11 @@ namespace Dashboard.Drawing
{ {
return null; return null;
} }
public int WriteParams(DrawQueue queue, object? obj, Span<byte> param)
{
return 0;
}
} }
public sealed class DrawCommand<T> : IDrawCommand<T> public sealed class DrawCommand<T> : IDrawCommand<T>
@ -80,6 +89,16 @@ namespace Dashboard.Drawing
return t; return t;
} }
public int WriteParams(DrawQueue queue, T? obj, Span<byte> param)
{
return obj!.Serialize(queue, param);
}
int IDrawCommand.WriteParams(DrawQueue queue, object? obj, Span<byte> param)
{
return WriteParams(queue, (T?)obj, param);
}
object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan<byte> param) object? IDrawCommand.GetParams(DrawQueue queue, ReadOnlySpan<byte> param)
{ {
return GetParams(queue, param); return GetParams(queue, param);

@ -64,9 +64,53 @@ namespace Dashboard.Drawing
public static void Point(this DrawQueue queue, Vector3 position, float size, IBrush brush) public static void Point(this DrawQueue queue, Vector3 position, float size, IBrush brush)
{ {
var controller = queue.GetController(DbBaseCommands.Instance); Vector3 radius = new Vector3(size / 2f);
controller.EnsureSize(position); Box3d bounds = new Box3d(position - radius, position + radius);
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
controller.EnsureBounds(bounds);
controller.Write(DbBaseCommands.Instance.DrawPoint, new PointCommandArgs(position, size, brush)); controller.Write(DbBaseCommands.Instance.DrawPoint, new PointCommandArgs(position, size, brush));
} }
public static void Line(this DrawQueue queue, Vector3 start, Vector3 end, float size, IBrush brush)
{
Vector3 radius = new Vector3(size / 2f);
Vector3 min = Vector3.Min(start, end) - radius;
Vector3 max = Vector3.Max(start, end) + radius;
Box3d bounds = new Box3d(min, max);
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
controller.EnsureBounds(bounds);
controller.Write(DbBaseCommands.Instance.DrawLine, new LineCommandArgs(start, end, size, brush));
}
public static void Rect(this DrawQueue queue, Vector3 start, Vector3 end, IBrush fillBrush)
{
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
Vector3 min = Vector3.Min(start, end);
Vector3 max = Vector3.Max(start, end);
controller.EnsureBounds(new Box3d(min, max));
controller.Write(DbBaseCommands.Instance.DrawRectF, new RectCommandArgs(start, end, fillBrush));
}
public static void Rect(this DrawQueue queue, Vector3 start, Vector3 end, IBrush strikeBrush, float strikeSize,
StrikeKind kind = StrikeKind.Center)
{
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
Vector3 min = Vector3.Min(start, end);
Vector3 max = Vector3.Max(start, end);
controller.EnsureBounds(new Box3d(min, max));
controller.Write(DbBaseCommands.Instance.DrawRectF, new RectCommandArgs(start, end, strikeBrush, strikeSize, kind));
}
public static void Rect(this DrawQueue queue, Vector3 start, Vector3 end, IBrush fillBrush, IBrush strikeBrush,
float strikeSize, StrikeKind kind = StrikeKind.Center)
{
IDrawController controller = queue.GetController(DbBaseCommands.Instance);
Vector3 min = Vector3.Min(start, end);
Vector3 max = Vector3.Max(start, end);
controller.EnsureBounds(new Box3d(min, max));
controller.Write(DbBaseCommands.Instance.DrawRectF, new RectCommandArgs(start, end, fillBrush, strikeBrush, strikeSize, kind));
}
} }
} }

@ -1,12 +1,14 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.IO; using System.IO;
using System.Numerics; using System.Runtime.CompilerServices;
namespace Dashboard.Drawing namespace Dashboard.Drawing
{ {
public class DrawQueue : IDisposable public class DrawQueue : IEnumerable<ICommandFrame>, IDisposable
{ {
private readonly HashList<IDrawExtension> _extensions = new HashList<IDrawExtension>(); private readonly HashList<IDrawExtension> _extensions = new HashList<IDrawExtension>();
private readonly HashList<IDrawCommand> _commands = new HashList<IDrawCommand>(); private readonly HashList<IDrawCommand> _commands = new HashList<IDrawCommand>();
@ -15,9 +17,9 @@ namespace Dashboard.Drawing
private readonly MemoryStream _commandStream = new MemoryStream(); private readonly MemoryStream _commandStream = new MemoryStream();
/// <summary> /// <summary>
/// Size of the image in points. /// The absolute boundary of all graphics objects.
/// </summary> /// </summary>
public Vector3 Size { get; private set; } = Vector3.Zero; public Box3d Bounds { get; set; }
/// <summary> /// <summary>
/// The extensions required to draw the image. /// The extensions required to draw the image.
@ -91,7 +93,7 @@ namespace Dashboard.Drawing
sz = ToVlq(cmdIndex, cmd); sz = ToVlq(cmdIndex, cmd);
cmd[sz++] = 0; cmd[sz++] = 0;
} }
_commandStream.Write(cmd[..sz]); _commandStream.Write(cmd[..sz]);
} }
@ -119,6 +121,10 @@ namespace Dashboard.Drawing
} }
} }
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<ICommandFrame> IEnumerable<ICommandFrame>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
private static int ToVlq(int value, Span<byte> bytes) private static int ToVlq(int value, Span<byte> bytes)
{ {
if (value < 0) if (value < 0)
@ -126,34 +132,43 @@ namespace Dashboard.Drawing
else if (bytes.Length < 5) else if (bytes.Length < 5)
throw new ArgumentOutOfRangeException(nameof(bytes), "Must at least be five bytes long."); throw new ArgumentOutOfRangeException(nameof(bytes), "Must at least be five bytes long.");
int i; if (value == 0)
{
bytes[0] = 0;
return 1;
}
int i;
for (i = 0; i < 5 && value != 0; i++, value >>= 7) for (i = 0; i < 5 && value != 0; i++, value >>= 7)
{ {
if (i > 0) if (i > 0)
bytes[i - 1] |= 1 << 7; bytes[i - 1] |= 1 << 7;
bytes[i] = (byte)(value & 0x7F); bytes[i] = (byte)(value & 0x7F);
} }
return i; return i;
} }
private static int FromVlq(ReadOnlySpan<byte> bytes) private static int FromVlq(ReadOnlySpan<byte> bytes, out int value)
{ {
int value = 0; value = 0;
for (int i = 0; i < bytes.Length; i++) int i;
for (i = 0; i < bytes.Length; i++)
{ {
byte b = bytes[i]; byte b = bytes[i];
value = (value << 7) | b; value = (value << 7) | b;
if ((b & (1 << 7)) == 0) if ((b & (1 << 7)) == 0)
{
i++;
break; break;
}
} }
return value; return i;
} }
public void Dispose() public void Dispose()
@ -163,19 +178,9 @@ namespace Dashboard.Drawing
private class DrawController(DrawQueue Queue) : IDrawController private class DrawController(DrawQueue Queue) : IDrawController
{ {
public void EnsureSize(Vector3 size) public void EnsureBounds(Box3d bounds)
{ {
Queue.Size = Vector3.Max(Queue.Size, size); Queue.Bounds = Box3d.Union(Queue.Bounds, bounds);
}
public int Require(IDrawExtension extension)
{
return Queue.RequireExtension(extension);
}
public int GetResourceIndex(IDrawResource resource)
{
return Queue._resources.Intern(resource);
} }
public void Write(IDrawCommand command) public void Write(IDrawCommand command)
@ -196,49 +201,142 @@ namespace Dashboard.Drawing
param.Serialize(Queue, bytes); param.Serialize(Queue, bytes);
Write(command, bytes); Write(command, bytes);
} }
public void Write<T1, T2>(T2 command, T1 param) where T2 : IDrawCommand<T1>
{
int length = command.WriteParams(Queue, param, Span<byte>.Empty);
Span<byte> bytes = stackalloc byte[length];
command.WriteParams(Queue, param, bytes);
Write(command, bytes);
}
} }
private class HashList<T> : IReadOnlyList<T> public class Enumerator : ICommandFrame, IEnumerator<ICommandFrame>
where T : notnull
{ {
private readonly List<T> _list = new List<T>(); private readonly DrawQueue _queue;
private readonly Dictionary<T, int> _map = new Dictionary<T, int>(); private readonly byte[] _stream;
private int _length;
private int _index = -1;
private int _paramsIndex = -1;
private int _paramLength = 0;
private IDrawCommand? _current = null;
public T this[int index] => _list[index]; public ICommandFrame Current => this;
public int Count => _list.Count; object? IEnumerator.Current => Current;
public int Intern(T value) public IDrawCommand Command => _current ?? throw new InvalidOperationException();
public bool HasParameters { get; private set; }
public Enumerator(DrawQueue queue)
{ {
if (_map.TryGetValue(value, out int index)) _queue = queue;
return index; _stream = queue._commandStream.GetBuffer();
_length = (int)queue._commandStream.Length;
index = Count;
_list.Add(value);
_map.Add(value, index);
return index;
} }
public void Clear() public bool MoveNext()
{ {
_list.Clear(); if (_index >= _length)
_map.Clear(); return false;
if (_index == -1)
_index = 0;
_index += FromVlq(_stream[_index .. (_index + 5)], out int command);
_current = _queue.Command[command];
HasParameters = _current.Length != 0;
if (!HasParameters)
{
_paramsIndex = -1;
return true;
}
int length;
if (_current.Length < 0)
{
_index += FromVlq(_stream[_index .. (_index + 5)], out length);
}
else
{
length = _current.Length;
}
_paramsIndex = _index;
_paramLength = length;
_index += length;
return true;
} }
public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); public void Reset()
IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); {
_index = -1;
_current = null;
}
public object? GetParameter()
{
return _current?.GetParams(_queue, _stream.AsSpan(_paramsIndex, _paramLength));
}
public T GetParameter<T>()
{
if (_current is IDrawCommand<T> command)
{
return command.GetParams(_queue, _stream.AsSpan(_paramsIndex, _paramLength))!;
}
else
{
throw new InvalidOperationException();
}
}
public bool TryGetParameter<T>([NotNullWhen(true)] out T? parameter)
{
if (_current is IDrawCommand<T> command)
{
parameter = command.GetParams(_queue, _stream.AsSpan(_paramsIndex, _paramLength))!;
return true;
}
else
{
parameter = default;
return false;
}
}
public void Dispose()
{
}
} }
} }
public interface ICommandFrame
{
public IDrawCommand Command { get; }
public bool HasParameters { get; }
public object? GetParameter();
public T GetParameter<T>();
public bool TryGetParameter<T>([NotNullWhen(true)] out T? parameter);
}
public interface IDrawController public interface IDrawController
{ {
/// <summary> /// <summary>
/// Ensures that the canvas is at least a certain size. /// Ensures that the canvas is at least a certain size.
/// </summary> /// </summary>
/// <param name="size">The minimum size.</param> /// <param name="bounds">The bounding box.</param>
void EnsureSize(Vector3 size); void EnsureBounds(Box3d bounds);
/// <summary> /// <summary>
/// Write into the command stream. /// Write into the command stream.
@ -259,5 +357,12 @@ namespace Dashboard.Drawing
/// <param name="command">The command to write.</param> /// <param name="command">The command to write.</param>
/// <param name="param">Any data associated with the command.</param> /// <param name="param">Any data associated with the command.</param>
void Write<T>(IDrawCommand command, T param) where T : IParameterSerializer<T>; void Write<T>(IDrawCommand command, T param) where T : IParameterSerializer<T>;
/// <summary>
/// Write into the command stream.
/// </summary>
/// <param name="command">The command to write.</param>
/// <param name="param">Any data associated with the command.</param>
void Write<T1, T2>(T2 command, T1 param) where T2 : IDrawCommand<T1>;
} }
} }

@ -1,4 +1,5 @@
using System.Drawing; using System;
using System.Drawing;
namespace Dashboard.Drawing namespace Dashboard.Drawing
{ {
@ -22,6 +23,11 @@ namespace Dashboard.Drawing
{ {
Color = color; Color = color;
} }
public override int GetHashCode()
{
return HashCode.Combine(Kind, Color);
}
} }
public class SolidBrushExtension : DrawExtension public class SolidBrushExtension : DrawExtension

@ -15,6 +15,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dashboard.TestApplication",
{49A62F46-AC1C-4240-8615-020D4FBBF964} = {49A62F46-AC1C-4240-8615-020D4FBBF964} {49A62F46-AC1C-4240-8615-020D4FBBF964} = {49A62F46-AC1C-4240-8615-020D4FBBF964}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Common", "Dashboard.Common\Dashboard.Common.csproj", "{C77CDD2B-2482-45F9-B330-47A52F5F13C0}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -33,6 +35,10 @@ Global
{7C90B90B-DF31-439B-9080-CD805383B014}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C90B90B-DF31-439B-9080-CD805383B014}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C90B90B-DF31-439B-9080-CD805383B014}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C90B90B-DF31-439B-9080-CD805383B014}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C90B90B-DF31-439B-9080-CD805383B014}.Release|Any CPU.Build.0 = Release|Any CPU {7C90B90B-DF31-439B-9080-CD805383B014}.Release|Any CPU.Build.0 = Release|Any CPU
{C77CDD2B-2482-45F9-B330-47A52F5F13C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C77CDD2B-2482-45F9-B330-47A52F5F13C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C77CDD2B-2482-45F9-B330-47A52F5F13C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C77CDD2B-2482-45F9-B330-47A52F5F13C0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

@ -1,6 +0,0 @@
namespace Dashboard;
public class Class1
{
}

@ -5,6 +5,19 @@ using System.Numerics;
DrawQueue queue = new DrawQueue(); DrawQueue queue = new DrawQueue();
queue.Point(Vector3.Zero, 2, new SolidBrush(Color.White)); SolidBrush fg = new SolidBrush(Color.Black);
SolidBrush bg = new SolidBrush(Color.White);
queue.Point(Vector3.Zero, 2f, fg);
queue.Line(Vector3.Zero, Vector3.One * 2, 2f, bg);
queue.Rect(Vector3.UnitX, 2 * Vector3.UnitX + Vector3.UnitY, fg, bg, 2f);
foreach (ICommandFrame frame in queue)
{
Console.WriteLine("{0}", frame.Command.Name);
if (frame.HasParameters)
Console.WriteLine("Param: {0}", frame.GetParameter());
}
Debugger.Break(); Debugger.Break();