NrxDebugVisualizer shows objects.
TODO: Update SceneTreeView after adding new objects
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
namespace NamedPipes;
|
||||
|
||||
public interface IDebugVisualizer
|
||||
|
||||
@@ -10,18 +10,25 @@ namespace NamedPipes;
|
||||
/// </summary>
|
||||
public class PipeSerializer : IPipeSerializer
|
||||
{
|
||||
private JsonSerializerOptions options = new()
|
||||
{
|
||||
IncludeFields = true
|
||||
};
|
||||
|
||||
public object Deserialize(byte[] data, Type type)
|
||||
{
|
||||
return JsonSerializer.Deserialize(data, type) ?? throw new InvalidDataException($"Can not deserialize to type: {type} ");
|
||||
|
||||
var obj = JsonSerializer.Deserialize(data, type, options) ?? throw new InvalidDataException($"Can not deserialize to type: {type} ");
|
||||
return obj;
|
||||
}
|
||||
|
||||
public byte[] Serialize(object o)
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
using (var utf8JsonWriter = new Utf8JsonWriter(memoryStream))
|
||||
{
|
||||
JsonSerializer.Serialize(utf8JsonWriter, o);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
using var memoryStream = new MemoryStream();
|
||||
using var utf8JsonWriter = new Utf8JsonWriter(memoryStream);
|
||||
JsonSerializer.Serialize(utf8JsonWriter, o, options);
|
||||
var data = memoryStream.ToArray();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
|
||||
|
||||
<Application.Styles>
|
||||
<FluentTheme />
|
||||
<SimpleTheme />
|
||||
<DockSimpleTheme />
|
||||
</Application.Styles>
|
||||
</Application>
|
||||
@@ -1,19 +1,21 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Data.Core;
|
||||
using Avalonia.Data.Core.Plugins;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Controls.Utilities;
|
||||
using NrxDebugVisualizer.ViewModels;
|
||||
using NrxDebugVisualizer.Views;
|
||||
using System.Linq;
|
||||
|
||||
namespace NrxDebugVisualizer;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public static Settings? Settings { get; set; }
|
||||
public override void Initialize()
|
||||
{
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
#if DEBUG
|
||||
this.AttachDeveloperTools();
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void OnFrameworkInitializationCompleted()
|
||||
@@ -24,10 +26,9 @@ public partial class App : Application
|
||||
{
|
||||
DataContext = new MainWindowViewModel(),
|
||||
};
|
||||
}
|
||||
Settings = Settings.Load(desktop.MainWindow);
|
||||
|
||||
}
|
||||
base.OnFrameworkInitializationCompleted();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,22 +1,25 @@
|
||||
using NamedPipes;
|
||||
using NrxDebugVisualizer.Scenes;
|
||||
using Num.Roto.Visualization.Math.Geometry;
|
||||
using Num.Roto.Visualization.Math.Utilities;
|
||||
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)
|
||||
private Action<VisualizerModel> UpdateAction { get; init; }
|
||||
public PipeServer(string pipeName, Action<VisualizerModel> updateAction,CancellationToken cancellationToken)
|
||||
{
|
||||
PipeName = pipeName;
|
||||
DebugVisualizerScene = debuggerScene;
|
||||
UpdateAction = updateAction;
|
||||
StartPipeServer(cancellationToken);
|
||||
}
|
||||
private void NamedPipesServer_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
@@ -24,56 +27,17 @@ internal sealed class PipeServer
|
||||
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);
|
||||
UpdateAction(visualizerObject);
|
||||
}
|
||||
|
||||
#region NamedPipesServer
|
||||
private async void StartPipeServer(CancellationToken cancellationToken)
|
||||
{
|
||||
NamedPipesServer = new NamedPipesServer(PipeName, logger: Console.WriteLine);
|
||||
NamedPipesServer = new NamedPipesServer(PipeName, logger: (s) => Log.Info(s,0));
|
||||
NamedPipesServer.PropertyChanged += NamedPipesServer_PropertyChanged;
|
||||
await NamedPipesServer.RunAsync(cancellationToken);
|
||||
await NamedPipesServer.RunAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
#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
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Num.Roto.Visualization\Avalonia\Controls\Controls.csproj" />
|
||||
<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" />
|
||||
|
||||
@@ -1,8 +1,127 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Controls.ViewModels;
|
||||
using NamedPipes;
|
||||
using NrxDebugVisualizer.Models;
|
||||
using NrxDebugVisualizer.Scenes;
|
||||
using Num.Roto.Visualization.Math.Geometry;
|
||||
using Num.Roto.Visualization.Math.Utilities;
|
||||
using Num.Roto.Visualization.Types;
|
||||
using Num.Roto.Visualization.VulkanLib.SceneInteraction;
|
||||
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.ViewModels;
|
||||
|
||||
public partial class MainWindowViewModel : ObservableObject
|
||||
internal sealed partial class MainWindowViewModel : ViewModelBase
|
||||
{
|
||||
public string Greeting { get; } = "Welcome to Avalonia!";
|
||||
public ProportionalDockControlViewModel ProportionalDockControlViewModel { get; } = new();
|
||||
public VulkanSceneControlViewModel VulkanSceneControlViewModel => ProportionalDockControlViewModel.VulkanSceneControlViewModel;
|
||||
public SceneTreeViewViewModel SceneTreeViewViewModel => ProportionalDockControlViewModel.SceneTreeViewViewModel?? throw new InvalidOperationException("SceneTreeViewViewModel is not available");
|
||||
public SceneInteraction SceneInteraction => ProportionalDockControlViewModel.SceneInteraction;
|
||||
|
||||
public DebugVisualizerScene DebugVisualizerScene => ProportionalDockControlViewModel.Scene as DebugVisualizerScene ?? throw new InvalidOperationException("Scene is not a DebugVisualizerScene");
|
||||
|
||||
private PipeServer PipeServer {get;init;}
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
ProportionalDockControlViewModel.Scene = new DebugVisualizerScene();
|
||||
ProportionalDockControlViewModel.Scene.SelectedCamera.DefaultFrame = CameraView.Frame(ViewPosition);
|
||||
PipeServer = new PipeServer(nameof(NrxDebugVisualizer), UpdateScene, cancellationToken: CancellationToken.None);
|
||||
}
|
||||
|
||||
#region Commands
|
||||
[RelayCommand]
|
||||
public static void ShowLogViewer()
|
||||
{
|
||||
Log.LogViewerEnabled = true;
|
||||
}
|
||||
[RelayCommand]
|
||||
private static void Exit(object? parameter)
|
||||
{
|
||||
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopApp)
|
||||
{
|
||||
desktopApp.Shutdown();
|
||||
}
|
||||
}
|
||||
[RelayCommand]
|
||||
internal void ResetCamera()
|
||||
{
|
||||
ProportionalDockControlViewModel.VulkanSceneControlViewModel.ResetCamera(CameraView.Frame(ViewPosition));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
internal void FitToWindow()
|
||||
{
|
||||
ProportionalDockControlViewModel.Scene?.FitToWindow(0.95f);
|
||||
ProportionalDockControlViewModel.VulkanSceneControlViewModel.SceneInteraction.Update();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Camera View
|
||||
public static IEnumerable<CameraView.Position> ViewPositions => Enum.GetValues<CameraView.Position>();
|
||||
private CameraView.Position _viewPosition = CameraView.Position.Custom1;
|
||||
public CameraView.Position ViewPosition
|
||||
{
|
||||
get => _viewPosition;
|
||||
set
|
||||
{
|
||||
if (_viewPosition == value) return;
|
||||
_viewPosition = value;
|
||||
OnPropertyChanged();
|
||||
if (_viewPosition == CameraView.Position.User) return;
|
||||
ProportionalDockControlViewModel.Scene?.SelectedCamera.DefaultFrame = CameraView.Frame(_viewPosition);
|
||||
FitToWindow();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Pipe Server
|
||||
private void UpdateScene(VisualizerModel visualizerModel)
|
||||
{
|
||||
SetPoint(visualizerModel.Point);
|
||||
SetPointList(visualizerModel.PointArray);
|
||||
SetFrame(visualizerModel.Frame);
|
||||
SetFrameList(visualizerModel.FrameArray);
|
||||
SceneInteraction.Update();
|
||||
}
|
||||
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, 5f);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -6,14 +6,45 @@
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="NrxDebugVisualizer.Views.MainWindow"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
xmlns:viewModels="clr-namespace:NrxDebugVisualizer.ViewModels"
|
||||
xmlns:controls="clr-namespace:Controls.Views;assembly=Controls"
|
||||
Icon="/Assets/avalonia-logo.ico"
|
||||
Title="NrxDebugVisualizer">
|
||||
Title="NrxDebugVisualizer"
|
||||
Width="1200"
|
||||
Height="800"
|
||||
MinWidth="800"
|
||||
MinHeight="600">
|
||||
|
||||
<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/>
|
||||
<viewModels:MainWindowViewModel />
|
||||
</Design.DataContext>
|
||||
<DockPanel LastChildFill="True" Background="LightGray" >
|
||||
<Menu DockPanel.Dock="Top" FontSize="12">
|
||||
<MenuItem Header="_File">
|
||||
<MenuItem Header="_Exit" Command="{Binding ExitCommand}" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Options" VerticalAlignment="Center">
|
||||
<MenuItem Header="_ShowLogViewer" Command="{Binding ShowLogViewerCommand}" />
|
||||
</MenuItem>
|
||||
<MenuItem Header="_Camera">
|
||||
<MenuItem Header="_Reset" Command="{Binding ResetCameraCommand }" />
|
||||
<MenuItem Header="_Fit" Command="{Binding FitToWindowCommand}" />
|
||||
<DropDownButton Content="View">
|
||||
<Button.Flyout>
|
||||
<Flyout Placement="Right">
|
||||
|
||||
<ListBox ItemsSource="{Binding ViewPositions}" SelectedItem="{Binding ViewPosition}" SelectionMode="Single" />
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</DropDownButton>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" Background="DarkGray" >
|
||||
<Label Content="{Binding ProportionalDockControlViewModel.VulkanSceneControlViewModel.Fps,FallbackValue=Unset}" IsHitTestVisible="False" VerticalContentAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" />
|
||||
<Label Content="{Binding ProportionalDockControlViewModel.VulkanSceneControlViewModel.ObjectInfo,FallbackValue=Unset}" IsHitTestVisible="False" VerticalContentAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Stretch" />
|
||||
</StackPanel>
|
||||
<controls:ProportionalDockControl DataContext="{Binding ProportionalDockControlViewModel}"/>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using NrxDebugVisualizer.ViewModels;
|
||||
using Num.Roto.Visualization.Types;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
|
||||
namespace NrxDebugVisualizer.Views;
|
||||
|
||||
@@ -6,6 +12,29 @@ public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
|
||||
InitializeComponent();
|
||||
|
||||
Closing += OnClosing;
|
||||
|
||||
}
|
||||
protected override void OnLoaded(RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(e);
|
||||
if (DataContext is not MainWindowViewModel viewModel) throw new DataException("wrong viewModel type!");
|
||||
if (App.Settings?.CameraFrame is not { } cameraFrame) return;
|
||||
var scene = viewModel.ProportionalDockControlViewModel.Scene;
|
||||
var camera = scene?.SelectedCamera;
|
||||
camera?.Frame = cameraFrame;
|
||||
viewModel.ViewPosition = CameraView.Position.User;
|
||||
}
|
||||
private async void OnClosing(object? sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
if (DataContext is not MainWindowViewModel viewModel) throw new DataException("wrong viewModel type!");
|
||||
var scene = viewModel.ProportionalDockControlViewModel.Scene;
|
||||
var camera = scene?.SelectedCamera;
|
||||
App.Settings?.MainWindow = this;
|
||||
App.Settings?.CameraFrame = camera?.Frame;
|
||||
App.Settings?.Save();
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,12 @@ VisualizerModel visualizerModel = new()
|
||||
};
|
||||
while (true)
|
||||
{
|
||||
var pipeClient = new NamedPipeClient("TestPipe", @".\","TestServer",logger:Console.WriteLine);
|
||||
var serverName = "NrxDebugVisualizer";
|
||||
var pipeClient = new NamedPipeClient(serverName, @".\", serverName, logger:Console.WriteLine);
|
||||
Thread.Sleep(1000);
|
||||
visualizerModel.Point = new System.Numerics.Vector3(count, count * 2, count * 3);
|
||||
count++;
|
||||
await pipeClient.SetVisualizerModelAsync(visualizerModel);
|
||||
pipeClient.SetMessageAsync($"Hello from TestClient " + count++).GetAwaiter().GetResult();
|
||||
//pipeClient.SetDebugObjectAsync(new DebugObject{Type = typeof(string).FullName,Data = Encoding.GetEncoding("UTF-8").GetBytes(message)}).GetAwaiter().GetResult();
|
||||
//await pipeClient.SetMessageAsync($"Hello from TestClient " + count++);
|
||||
pipeClient.Dispose();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
var pipeServer = new NamedPipes.NamedPipesServer("TestPipe", logger:Console.WriteLine);
|
||||
var pipeServer = new NamedPipes.NamedPipesServer("NrxDebugVisualizer", logger:Console.WriteLine);
|
||||
await pipeServer.RunAsync(CancellationToken.None).ConfigureAwait(true);
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"profiles": {
|
||||
"TestServer": {
|
||||
"commandName": "Project",
|
||||
"workingDirectory": "C:\\Users\\heilm\\Documents\\Visual Studio 2026\\Visualizers"
|
||||
"workingDirectory": "C:\\Users\\matth\\Documents\\Visual Studio 2026\\Visualizers"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user