diff --git a/DebugVisualizerExtension.slnx b/DebugVisualizerExtension.slnx
index 3d880db..a4cd754 100644
--- a/DebugVisualizerExtension.slnx
+++ b/DebugVisualizerExtension.slnx
@@ -7,9 +7,15 @@
+
+
+
+
+
+
diff --git a/NamedPipes/NamedPipeServer.cs b/NamedPipes/NamedPipeServer.cs
index d4621b0..07733f3 100644
--- a/NamedPipes/NamedPipeServer.cs
+++ b/NamedPipes/NamedPipeServer.cs
@@ -3,6 +3,7 @@ using PipeMethodCalls;
using System;
using System.Diagnostics;
+using System.Threading;
using System.Threading.Tasks;
namespace NamedPipes;
@@ -30,15 +31,15 @@ public partial class NamedPipesServer(string pipeName,Action? logger = n
Logger("Waiting for client connection...");
await PipeServer.WaitForConnectionAsync().ConfigureAwait(true);
}
- public async Task RunAsync()
+ public async Task RunAsync(CancellationToken cancellationToken)
{
- while(true)
+ while(!cancellationToken.IsCancellationRequested)
{
await WaitForConnectionAsync().ConfigureAwait(true);
Logger("Client connected.");
while (IsConnected)
{
- await Task.Delay(100).ConfigureAwait(true);
+ await Task.Delay(100, cancellationToken).ConfigureAwait(true);
}
Logger("Client disconnected.");
}
diff --git a/NrxDebugVisualizer/App.axaml b/NrxDebugVisualizer/App.axaml
new file mode 100644
index 0000000..cd9bb79
--- /dev/null
+++ b/NrxDebugVisualizer/App.axaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/NrxDebugVisualizer/App.axaml.cs b/NrxDebugVisualizer/App.axaml.cs
new file mode 100644
index 0000000..52a5be2
--- /dev/null
+++ b/NrxDebugVisualizer/App.axaml.cs
@@ -0,0 +1,47 @@
+using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Data.Core;
+using Avalonia.Data.Core.Plugins;
+using Avalonia.Markup.Xaml;
+using NrxDebugVisualizer.ViewModels;
+using NrxDebugVisualizer.Views;
+using System.Linq;
+
+namespace NrxDebugVisualizer;
+
+public partial class App : Application
+{
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ public override void OnFrameworkInitializationCompleted()
+ {
+ if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+ {
+ // Avoid duplicate validations from both Avalonia and the CommunityToolkit.
+ // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
+ DisableAvaloniaDataAnnotationValidation();
+ desktop.MainWindow = new MainWindow
+ {
+ DataContext = new MainWindowViewModel(),
+ };
+ }
+
+ base.OnFrameworkInitializationCompleted();
+ }
+
+ private static void DisableAvaloniaDataAnnotationValidation()
+ {
+ // Get an array of plugins to remove
+ var dataValidationPluginsToRemove =
+ BindingPlugins.DataValidators.OfType().ToArray();
+
+ // remove each entry found
+ foreach (var plugin in dataValidationPluginsToRemove)
+ {
+ BindingPlugins.DataValidators.Remove(plugin);
+ }
+ }
+}
\ No newline at end of file
diff --git a/NrxDebugVisualizer/Assets/avalonia-logo.ico b/NrxDebugVisualizer/Assets/avalonia-logo.ico
new file mode 100644
index 0000000..f7da8bb
Binary files /dev/null and b/NrxDebugVisualizer/Assets/avalonia-logo.ico differ
diff --git a/NrxDebugVisualizer/Models/PipeServer.cs b/NrxDebugVisualizer/Models/PipeServer.cs
new file mode 100644
index 0000000..710e5a0
--- /dev/null
+++ b/NrxDebugVisualizer/Models/PipeServer.cs
@@ -0,0 +1,79 @@
+using NamedPipes;
+using NrxDebugVisualizer.Scenes;
+using Num.Roto.Visualization.Math.Geometry;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using System.Threading;
+using KontractFrame = System.ValueTuple;
+namespace NrxDebugVisualizer.Models;
+
+internal sealed class PipeServer
+{
+ private string PipeName { get; }
+ private NamedPipesServer? NamedPipesServer { get; set; }
+ public PipeServer(string pipeName, DebugVisualizerScene debuggerScene,CancellationToken cancellationToken)
+ {
+ PipeName = pipeName;
+ DebugVisualizerScene = debuggerScene;
+ StartPipeServer(cancellationToken);
+ }
+ private void NamedPipesServer_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName != nameof(NamedPipesServer.VisualizerModel)) return;
+ var visualizerObject = NamedPipesServer?.VisualizerModel;
+ if (visualizerObject is null) return;
+
+ SetPoint(visualizerObject.Point);
+ SetPointList(visualizerObject.PointArray);
+ SetFrame(visualizerObject.Frame);
+ SetFrameList(visualizerObject.FrameArray);
+ }
+
+ #region NamedPipesServer
+ private async void StartPipeServer(CancellationToken cancellationToken)
+ {
+ NamedPipesServer = new NamedPipesServer(PipeName, logger: Console.WriteLine);
+ NamedPipesServer.PropertyChanged += NamedPipesServer_PropertyChanged;
+ await NamedPipesServer.RunAsync(cancellationToken);
+ }
+ #endregion
+
+ #region DebugVisualizerScene
+ private DebugVisualizerScene DebugVisualizerScene { get; }
+ private static Frame ToFrame(KontractFrame kontractFrame)
+ {
+ var (translation, orientation, scale) = kontractFrame;
+ return new Frame(translation, scale, orientation);
+ }
+ private static Frame[] ToFrameArray(IEnumerable frameList)
+ {
+ return [.. frameList.Select(ToFrame)];
+ }
+
+ private void SetPoint(Vector3? point)
+ {
+ if (point is null) return;
+ DebugVisualizerScene.AddPointGeometry(point.Value, 20f);
+ }
+
+ private void SetPointList(Vector3[]? pointList)
+ {
+ if (pointList == null || pointList.Length < 1) return;
+ DebugVisualizerScene.AddPointListGeometry(pointList, 5);
+ }
+
+ private void SetFrame(KontractFrame? kontractFrame)
+ {
+ if (kontractFrame is null) return;
+ DebugVisualizerScene.AddFrameGeometry(ToFrame(kontractFrame.Value), 1);
+ }
+
+ private void SetFrameList(KontractFrame[]? frameArray)
+ {
+ if (frameArray == null || frameArray.Length < 1) return;
+ DebugVisualizerScene.AddFrameListGeometry(ToFrameArray(frameArray), 1);
+ }
+ #endregion
+}
diff --git a/NrxDebugVisualizer/NrxDebugVisualizer.csproj b/NrxDebugVisualizer/NrxDebugVisualizer.csproj
new file mode 100644
index 0000000..60ffa21
--- /dev/null
+++ b/NrxDebugVisualizer/NrxDebugVisualizer.csproj
@@ -0,0 +1,31 @@
+
+
+ WinExe
+ net10.0
+ enable
+ app.manifest
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ None
+ All
+
+
+
+
+
+
+
+
+
diff --git a/NrxDebugVisualizer/Program.cs b/NrxDebugVisualizer/Program.cs
new file mode 100644
index 0000000..cf8e29d
--- /dev/null
+++ b/NrxDebugVisualizer/Program.cs
@@ -0,0 +1,21 @@
+using Avalonia;
+using System;
+
+namespace NrxDebugVisualizer;
+
+internal sealed class Program
+{
+ // Initialization code. Don't use any Avalonia, third-party APIs or any
+ // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
+ // yet and stuff might break.
+ [STAThread]
+ public static void Main(string[] args) => BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+
+ // Avalonia configuration, don't remove; also used by visual designer.
+ public static AppBuilder BuildAvaloniaApp()
+ => AppBuilder.Configure()
+ .UsePlatformDetect()
+ .WithInterFont()
+ .LogToTrace();
+}
diff --git a/NrxDebugVisualizer/Scenes/DebugVisualizerScene.cs b/NrxDebugVisualizer/Scenes/DebugVisualizerScene.cs
new file mode 100644
index 0000000..ba28087
--- /dev/null
+++ b/NrxDebugVisualizer/Scenes/DebugVisualizerScene.cs
@@ -0,0 +1,89 @@
+using Num.Roto.Visualization.Math.Geometry;
+using Num.Roto.Visualization.SceneGraph.Geometry;
+using Num.Roto.Visualization.SceneGraph.Material;
+using Num.Roto.Visualization.SceneGraph.Scene;
+using Num.Roto.Visualization.SceneGraph.Scene.Nodes;
+using Num.Roto.Visualization.Types;
+using System.Numerics;
+
+namespace NrxDebugVisualizer.Scenes;
+
+internal sealed class DebugVisualizerScene : Num.Roto.Visualization.SceneGraph.Scenes.SceneBase
+{
+ private static uint Counter;
+ private static uint Uid => Counter++;
+
+ private SceneNode Points { get; }
+ private SceneNode Frames { get; }
+
+
+ internal DebugVisualizerScene() : base("NrxDebuggerVisualizerScene")
+ {
+ Points = new SceneNode(nameof(Points), Objects);
+ Frames = new SceneNode(nameof(Frames), Objects);
+ }
+ public void AddPointGeometry(Vector3 position, float pointSize)
+ {
+ var point = new Points([position], "Point");
+ _ = new SceneGeometry("Point" + $".{Uid}", point, new Material(Colors4.White, null), Points)
+ {
+ PointSize = pointSize,
+ ComputeFragColor = false
+ };
+ }
+ public void AddPointListGeometry(Vector3[] positions, float pointSize)
+ {
+ var points = new Points(positions, "Points");
+ var material = new Material(Colors4.White, null)
+ {
+ EmissiveColor = Colors4.White,
+ VertexColorBlendFactor = 1
+ };
+ _ = new SceneGeometry("Points" + $".{Uid}", points, material, Points)
+ {
+ PointSize = pointSize,
+ ComputeFragColor = false
+ };
+ }
+ public void AddFrameGeometry(Frame frame, float length)
+ {
+ AddFrameListGeometry([frame], length);
+ }
+ public void AddFrameListGeometry(Frame[] frames, float length)
+ {
+ var material = new Material(Colors4.White, null)
+ {
+ EmissiveColor = Colors4.White,
+ VertexColorBlendFactor = 1
+ };
+ var name = frames.Length > 1 ? "FrameList" : "Frame";
+ var firstFrame = frames[0];
+ var frameContainer = new SceneNode(name + $".{Uid}", Frames)
+ {
+ Frame = firstFrame
+ };
+ var inverse = firstFrame.Inverse();
+ for (var i = 0; i < frames.Length; ++i)
+ {
+ frames[i] = inverse * frames[i];
+ }
+ var frameGeometry = new SceneGeometry("Geometry", Crosses.DefaultCross(0.5f), material, frameContainer)
+ {
+ InstanceFrames = [.. frames],
+ Flags = 0,
+ LineWidth = 1
+ };
+ if (frames.Length > 1)
+ {
+ var size = 2 * (frames.Length - 1);
+ var path = new Vector3[size];
+ path[0] = frames[0].Translation;
+ for (var i = 1; i < frames.Length - 1; ++i)
+ {
+ path[2 * i] = path[2 * i - 1] = frames[i].Translation;
+ }
+ path[^1] = frames[^1].Translation;
+ _ = new SceneGeometry("Path", new Lines(path), material, frameGeometry);
+ }
+ }
+}
diff --git a/NrxDebugVisualizer/ViewModels/MainWindowViewModel.cs b/NrxDebugVisualizer/ViewModels/MainWindowViewModel.cs
new file mode 100644
index 0000000..7535766
--- /dev/null
+++ b/NrxDebugVisualizer/ViewModels/MainWindowViewModel.cs
@@ -0,0 +1,8 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace NrxDebugVisualizer.ViewModels;
+
+public partial class MainWindowViewModel : ObservableObject
+{
+ public string Greeting { get; } = "Welcome to Avalonia!";
+}
diff --git a/NrxDebugVisualizer/Views/MainWindow.axaml b/NrxDebugVisualizer/Views/MainWindow.axaml
new file mode 100644
index 0000000..c93a8eb
--- /dev/null
+++ b/NrxDebugVisualizer/Views/MainWindow.axaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/NrxDebugVisualizer/Views/MainWindow.axaml.cs b/NrxDebugVisualizer/Views/MainWindow.axaml.cs
new file mode 100644
index 0000000..2f0f3a3
--- /dev/null
+++ b/NrxDebugVisualizer/Views/MainWindow.axaml.cs
@@ -0,0 +1,11 @@
+using Avalonia.Controls;
+
+namespace NrxDebugVisualizer.Views;
+
+public partial class MainWindow : Window
+{
+ public MainWindow()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/NrxDebugVisualizer/app.manifest b/NrxDebugVisualizer/app.manifest
new file mode 100644
index 0000000..a2ac8c1
--- /dev/null
+++ b/NrxDebugVisualizer/app.manifest
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TestClient/Program.cs b/TestClient/Program.cs
index c60f9dc..183d309 100644
--- a/TestClient/Program.cs
+++ b/TestClient/Program.cs
@@ -1,8 +1,8 @@
-using System.Text;
+
using NamedPipes;
var count = 0;
var message = "Hello World!".ToCharArray();
-VisualizerModel visualizerModel = new VisualizerModel
+VisualizerModel visualizerModel = new()
{
Content = "Hello from TestClient",
Point = new System.Numerics.Vector3(1, 2, 3),
@@ -13,7 +13,7 @@ VisualizerModel visualizerModel = new VisualizerModel
};
while (true)
{
- var pipeClient = new NamedPipeClient("testPipe",@".\","TestServer",logger:Console.WriteLine);
+ var pipeClient = new NamedPipeClient("TestPipe", @".\","TestServer",logger:Console.WriteLine);
Thread.Sleep(1000);
await pipeClient.SetVisualizerModelAsync(visualizerModel);
pipeClient.SetMessageAsync($"Hello from TestClient " + count++).GetAwaiter().GetResult();
diff --git a/TestServer/Program.cs b/TestServer/Program.cs
index bd9d7cb..c115b97 100644
--- a/TestServer/Program.cs
+++ b/TestServer/Program.cs
@@ -1,5 +1,6 @@
using System;
-var pipeServer = new NamedPipes.NamedPipesServer("testPipe",logger:Console.WriteLine);
-await pipeServer.RunAsync().ConfigureAwait(true);
+using System.Threading;
+var pipeServer = new NamedPipes.NamedPipesServer("TestPipe", logger:Console.WriteLine);
+await pipeServer.RunAsync(CancellationToken.None).ConfigureAwait(true);