diff --git a/NamedPipes/IVisualizerKontract.cs b/NamedPipes/IVisualizerKontract.cs index ecc8fa6..51ff48a 100644 --- a/NamedPipes/IVisualizerKontract.cs +++ b/NamedPipes/IVisualizerKontract.cs @@ -1,6 +1,4 @@ -using System; -using System.Numerics; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace NamedPipes; public interface IDebugVisualizer diff --git a/NamedPipes/PipeSerializer.cs b/NamedPipes/PipeSerializer.cs index 45b136d..c42eab6 100644 --- a/NamedPipes/PipeSerializer.cs +++ b/NamedPipes/PipeSerializer.cs @@ -10,18 +10,25 @@ namespace NamedPipes; /// 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; } } diff --git a/NrxDebugVisualizer/App.axaml b/NrxDebugVisualizer/App.axaml index cd9bb79..9036f7c 100644 --- a/NrxDebugVisualizer/App.axaml +++ b/NrxDebugVisualizer/App.axaml @@ -3,8 +3,9 @@ x:Class="NrxDebugVisualizer.App" RequestedThemeVariant="Default"> - + - + + \ No newline at end of file diff --git a/NrxDebugVisualizer/App.axaml.cs b/NrxDebugVisualizer/App.axaml.cs index 96c58a4..6f7ca60 100644 --- a/NrxDebugVisualizer/App.axaml.cs +++ b/NrxDebugVisualizer/App.axaml.cs @@ -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(); } - - -} \ No newline at end of file +} diff --git a/NrxDebugVisualizer/Models/PipeServer.cs b/NrxDebugVisualizer/Models/PipeServer.cs index 710e5a0..6b1be11 100644 --- a/NrxDebugVisualizer/Models/PipeServer.cs +++ b/NrxDebugVisualizer/Models/PipeServer.cs @@ -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; + 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 UpdateAction { get; init; } + public PipeServer(string pipeName, Action 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 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 index 132e447..a8f3cff 100644 --- a/NrxDebugVisualizer/NrxDebugVisualizer.csproj +++ b/NrxDebugVisualizer/NrxDebugVisualizer.csproj @@ -24,6 +24,7 @@ + diff --git a/NrxDebugVisualizer/ViewModels/MainWindowViewModel.cs b/NrxDebugVisualizer/ViewModels/MainWindowViewModel.cs index 7535766..db8fa0b 100644 --- a/NrxDebugVisualizer/ViewModels/MainWindowViewModel.cs +++ b/NrxDebugVisualizer/ViewModels/MainWindowViewModel.cs @@ -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; 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 ViewPositions => Enum.GetValues(); + 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 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 } + diff --git a/NrxDebugVisualizer/Views/MainWindow.axaml b/NrxDebugVisualizer/Views/MainWindow.axaml index 64030e1..c077543 100644 --- a/NrxDebugVisualizer/Views/MainWindow.axaml +++ b/NrxDebugVisualizer/Views/MainWindow.axaml @@ -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"> - - - + + + + + + + + + + + + + + + + - + + + + + + + + + + + + \ No newline at end of file diff --git a/NrxDebugVisualizer/Views/MainWindow.axaml.cs b/NrxDebugVisualizer/Views/MainWindow.axaml.cs index 2f0f3a3..543fc6a 100644 --- a/NrxDebugVisualizer/Views/MainWindow.axaml.cs +++ b/NrxDebugVisualizer/Views/MainWindow.axaml.cs @@ -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(); } } \ No newline at end of file diff --git a/TestClient/Program.cs b/TestClient/Program.cs index 183d309..9ff0b6b 100644 --- a/TestClient/Program.cs +++ b/TestClient/Program.cs @@ -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(); } diff --git a/TestServer/Program.cs b/TestServer/Program.cs index c115b97..ab288f3 100644 --- a/TestServer/Program.cs +++ b/TestServer/Program.cs @@ -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); diff --git a/TestServer/Properties/launchSettings.json b/TestServer/Properties/launchSettings.json index b0f0d47..5490bce 100644 --- a/TestServer/Properties/launchSettings.json +++ b/TestServer/Properties/launchSettings.json @@ -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" } } } \ No newline at end of file