Compare commits

..

3 Commits

Author SHA1 Message Date
Matthias Heil 30aa75dc8d Added NrxDebugVisualizer, not working 2026-04-08 16:06:35 +02:00
Matthias Heil 0f145eaebd Rename NrxDebuggerVisualizerProvider/NrxDebugVisualizerProvider.csproj (from NrxDebuggerVisualizerProvider/NrxDebugVisualizer.csproj)
Modified   DebugVisualizerExtension.slnx
2026-04-08 14:26:29 +02:00
Matthias Heil b56974ec06 Copy TestServer to Visual Studio 2026 2026-04-08 14:24:40 +02:00
18 changed files with 372 additions and 10 deletions
+7 -1
View File
@@ -7,8 +7,14 @@
<Project Path="TestServer/TestServer.csproj" Id="c81154ef-d854-4f1e-9d14-2cf674885291" /> <Project Path="TestServer/TestServer.csproj" Id="c81154ef-d854-4f1e-9d14-2cf674885291" />
<Project Path="Vector3VisualizerTest/Vector3VisualizerTest.csproj" /> <Project Path="Vector3VisualizerTest/Vector3VisualizerTest.csproj" />
</Folder> </Folder>
<Project Path="../Num.Roto.Visualization/Base/Num.Roto.Visualization.Base.csproj" />
<Project Path="../Num.Roto.Visualization/Math/Num.Roto.Visualization.Math.csproj" /> <Project Path="../Num.Roto.Visualization/Math/Num.Roto.Visualization.Math.csproj" />
<Project Path="NrxDebuggerVisualizerProvider/NrxDebugVisualizer.csproj"> <Project Path="../Num.Roto.Visualization/SceneGraph/Num.Roto.Visualization.SceneGraph.csproj" />
<Project Path="../Num.Roto.Visualization/Types/Num.Roto.Visualization.Types.csproj" />
<Project Path="NrxDebuggerVisualizerProvider/NrxDebugVisualizerProvider.csproj">
<Deploy />
</Project>
<Project Path="NrxDebugVisualizer/NrxDebugVisualizer.csproj" Id="807bb178-0fd6-4ae8-8c48-1088fa8a01fd">
<Deploy /> <Deploy />
</Project> </Project>
<Project Path="NrxVisualizerObjectSource/NrxVisualizerObjectSource.csproj" /> <Project Path="NrxVisualizerObjectSource/NrxVisualizerObjectSource.csproj" />
+4 -3
View File
@@ -3,6 +3,7 @@ using PipeMethodCalls;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NamedPipes; namespace NamedPipes;
@@ -30,15 +31,15 @@ public partial class NamedPipesServer(string pipeName,Action<string>? logger = n
Logger("Waiting for client connection..."); Logger("Waiting for client connection...");
await PipeServer.WaitForConnectionAsync().ConfigureAwait(true); await PipeServer.WaitForConnectionAsync().ConfigureAwait(true);
} }
public async Task RunAsync() public async Task RunAsync(CancellationToken cancellationToken)
{ {
while(true) while(!cancellationToken.IsCancellationRequested)
{ {
await WaitForConnectionAsync().ConfigureAwait(true); await WaitForConnectionAsync().ConfigureAwait(true);
Logger("Client connected."); Logger("Client connected.");
while (IsConnected) while (IsConnected)
{ {
await Task.Delay(100).ConfigureAwait(true); await Task.Delay(100, cancellationToken).ConfigureAwait(true);
} }
Logger("Client disconnected."); Logger("Client disconnected.");
} }
+10
View File
@@ -0,0 +1,10 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="NrxDebugVisualizer.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
+47
View File
@@ -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<DataAnnotationsValidationPlugin>().ToArray();
// remove each entry found
foreach (var plugin in dataValidationPluginsToRemove)
{
BindingPlugins.DataValidators.Remove(plugin);
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

+79
View File
@@ -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<System.Numerics.Vector3, System.Numerics.Quaternion, float>;
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<KontractFrame> 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
}
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.12" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.12" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.12" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.12" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.12">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Num.Roto.Visualization\Math\Num.Roto.Visualization.Math.csproj" />
<ProjectReference Include="..\..\Num.Roto.Visualization\SceneGraph\Num.Roto.Visualization.SceneGraph.csproj" />
<ProjectReference Include="..\NamedPipes\NamedPipes.csproj" />
</ItemGroup>
</Project>
+21
View File
@@ -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<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
@@ -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);
}
}
}
@@ -0,0 +1,8 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace NrxDebugVisualizer.ViewModels;
public partial class MainWindowViewModel : ObservableObject
{
public string Greeting { get; } = "Welcome to Avalonia!";
}
+20
View File
@@ -0,0 +1,20 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:NrxDebugVisualizer.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="NrxDebugVisualizer.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
Icon="/Assets/avalonia-logo.ico"
Title="NrxDebugVisualizer">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainWindowViewModel/>
</Design.DataContext>
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Window>
@@ -0,0 +1,11 @@
using Avalonia.Controls;
namespace NrxDebugVisualizer.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="NrxDebugVisualizer.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
+3 -3
View File
@@ -1,8 +1,8 @@
using System.Text;
using NamedPipes; using NamedPipes;
var count = 0; var count = 0;
var message = "Hello World!".ToCharArray(); var message = "Hello World!".ToCharArray();
VisualizerModel visualizerModel = new VisualizerModel VisualizerModel visualizerModel = new()
{ {
Content = "Hello from TestClient", Content = "Hello from TestClient",
Point = new System.Numerics.Vector3(1, 2, 3), Point = new System.Numerics.Vector3(1, 2, 3),
@@ -13,7 +13,7 @@ VisualizerModel visualizerModel = new VisualizerModel
}; };
while (true) while (true)
{ {
var pipeClient = new NamedPipeClient("testPipe",@".\","TestServer",logger:Console.WriteLine); var pipeClient = new NamedPipeClient("TestPipe", @".\","TestServer",logger:Console.WriteLine);
Thread.Sleep(1000); Thread.Sleep(1000);
await pipeClient.SetVisualizerModelAsync(visualizerModel); await pipeClient.SetVisualizerModelAsync(visualizerModel);
pipeClient.SetMessageAsync($"Hello from TestClient " + count++).GetAwaiter().GetResult(); pipeClient.SetMessageAsync($"Hello from TestClient " + count++).GetAwaiter().GetResult();
+3 -2
View File
@@ -1,5 +1,6 @@
using System; using System;
var pipeServer = new NamedPipes.NamedPipesServer("testPipe",logger:Console.WriteLine); using System.Threading;
await pipeServer.RunAsync().ConfigureAwait(true); var pipeServer = new NamedPipes.NamedPipesServer("TestPipe", logger:Console.WriteLine);
await pipeServer.RunAsync(CancellationToken.None).ConfigureAwait(true);
@@ -0,0 +1,8 @@
{
"profiles": {
"TestServer": {
"commandName": "Project",
"workingDirectory": "C:\\Users\\heilm\\Documents\\Visual Studio 2026\\Visualizers"
}
}
}
+13 -1
View File
@@ -8,5 +8,17 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\NamedPipes\NamedPipes.csproj" /> <ProjectReference Include="..\NamedPipes\NamedPipes.csproj" />
</ItemGroup> </ItemGroup>
<Target Name="CopyToVs2026" Condition="Exists('$(OutputPath)\$(MSBuildProjectName).exe')" AfterTargets="AfterBuild" >
<Message Importance="High" Text="CopyToVs2026"></Message>
<ItemGroup>
<FilesToCopy Include="$(OutputPath)\$(MSBuildProjectName).exe;
$(OutputPath)\$(MSBuildProjectName).dll;
$(OutputPath)\$(MSBuildProjectName).runtimeconfig.json;
$(OutputPath)\PipeMethodCalls.dll;
$(OutputPath)\NamedPipes.dll;
$(OutputPath)\CommunityToolkit.Mvvm.dll;
"/>
</ItemGroup>
<Copy SourceFiles="@(FilesToCopy)" DestinationFolder="$(USERPROFILE)\Documents\Visual Studio 2026\Visualizers" SkipUnchangedFiles="True" />
</Target>
</Project> </Project>