diff --git a/Disp/RenderingCanvas.cs b/Disp/RenderingCanvas.cs index dfa27ec..48248d3 100644 --- a/Disp/RenderingCanvas.cs +++ b/Disp/RenderingCanvas.cs @@ -11,464 +11,463 @@ using System.Collections.Generic; using Hi.Geom; using Microsoft.Win32; // Add this for SystemEvents -namespace Hi.WpfPlus.Disp +namespace Hi.WpfPlus.Disp; + +#region WPF Rendering Canvas +/// +/// Provides a WPF rendering canvas for 3D visualization of HiAPI components. +/// Handles user interactions, rendering, and integration with the DispEngine system. +/// +/// +/// This canvas provides the core rendering capabilities for WPF applications using HiAPI. +/// It manages mouse, keyboard, and touch events, and transforms them into appropriate +/// actions in the 3D environment. +/// +public class RenderingCanvas : UserControl, IDisposable { - #region WPF Rendering Canvas - /// - /// Provides a WPF rendering canvas for 3D visualization of HiAPI components. - /// Handles user interactions, rendering, and integration with the DispEngine system. - /// - /// - /// This canvas provides the core rendering capabilities for WPF applications using HiAPI. - /// It manages mouse, keyboard, and touch events, and transforms them into appropriate - /// actions in the 3D environment. - /// - public class RenderingCanvas : UserControl, IDisposable - { - #region Core_Properties - /// - /// The DispEngine instance that handles rendering and user interactions - /// - public DispEngine DispEngine { get; } = new DispEngine(); - - /// - /// Internal container for rendering content - /// - private UserControl DisplayerPane { get; } - - /// - /// Dictionary to store touch point information - /// - private Dictionary TouchingPointsMap { get; } = new Dictionary(); - - /// - /// Dictionary to store previous positions of touch points - /// - private Dictionary PreviousTouchingPointsMap { get; } = new Dictionary(); - #endregion - - #region Power_Management - /// - /// Handles system power mode changes - /// - private void InitializePowerManagement() - { - SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; - } + #region Core_Properties + /// + /// The DispEngine instance that handles rendering and user interactions + /// + public DispEngine DispEngine { get; } = new DispEngine(); - /// - /// Handles power mode change events - /// - private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) - { - switch (e.Mode) - { - case PowerModes.Suspend: - DispEngine.IsVisible = false; - break; - case PowerModes.Resume: - DispEngine.IsVisible = IsVisible; - break; - } - } - #endregion - - #region Initialization - /// - /// Initializes a new instance of the RenderingCanvas - /// - public RenderingCanvas() - { - DispEngine.BackgroundColor = new Vec3d(0.1, 0.1, 0.5); - DispEngine.BackgroundOpacity = 0.1; + /// + /// Internal container for rendering content + /// + private UserControl DisplayerPane { get; } - // Configure the main control properties - HorizontalAlignment = HorizontalAlignment.Stretch; - VerticalAlignment = VerticalAlignment.Stretch; - Focusable = true; - KeyboardNavigation.SetDirectionalNavigation(this, KeyboardNavigationMode.Cycle); - DataContextChanged += CanvasDataContextChanged; + /// + /// Dictionary to store touch point information + /// + private Dictionary TouchingPointsMap { get; } = new Dictionary(); - // Create and configure the display pane - DisplayerPane = new UserControl(); - DisplayerPane.HorizontalAlignment = HorizontalAlignment.Stretch; - DisplayerPane.VerticalAlignment = VerticalAlignment.Stretch; - DisplayerPane.Focusable = true; - DisplayerPane.IsTabStop = true; + /// + /// Dictionary to store previous positions of touch points + /// + private Dictionary PreviousTouchingPointsMap { get; } = new Dictionary(); + #endregion - // Connect event handlers for user input and window events - DisplayerPane.SizeChanged += RenderingCanvas_SizeChanged; - DisplayerPane.MouseMove += RenderingCanvas_MouseMove; - DisplayerPane.MouseDown += RenderingCanvas_MouseDown; - DisplayerPane.MouseUp += RenderingCanvas_MouseUp; - DisplayerPane.MouseWheel += RenderingCanvas_MouseWheel; - DisplayerPane.KeyDown += RenderingCanvas_KeyDown; - DisplayerPane.KeyUp += RenderingCanvas_KeyUp; - DisplayerPane.Loaded += RenderingCanvas_Loaded; - DisplayerPane.Unloaded += RenderingCanvas_Unloaded; - DisplayerPane.IsVisibleChanged += DisplayerPane_IsVisibleChanged; + #region Power_Management + /// + /// Handles system power mode changes + /// + private void InitializePowerManagement() + { + SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; + } - // Add touch event handlers - DisplayerPane.TouchDown += RenderingCanvas_TouchDown; - DisplayerPane.TouchMove += RenderingCanvas_TouchMove; - DisplayerPane.TouchUp += RenderingCanvas_TouchUp; - - // Enable touch support - this.IsManipulationEnabled = true; + /// + /// Handles power mode change events + /// + private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) + { + switch (e.Mode) + { + case PowerModes.Suspend: + DispEngine.IsVisible = false; + break; + case PowerModes.Resume: + DispEngine.IsVisible = IsVisible; + break; + } + } + #endregion - // Initialize power management - InitializePowerManagement(); + #region Initialization + /// + /// Initializes a new instance of the RenderingCanvas + /// + public RenderingCanvas() + { + DispEngine.BackgroundColor = new Vec3d(0.1, 0.1, 0.5); + DispEngine.BackgroundOpacity = 0.1; - // Add the display pane to this control's content - Content = DisplayerPane; - } - #endregion - - #region Touch_Events - /// - /// Handles the touch down event - /// - private void RenderingCanvas_TouchDown(object sender, TouchEventArgs e) - { - // Add touch point to dictionary - Point touchPoint = e.GetTouchPoint(DisplayerPane).Position; - DispEngine.TouchDown(e.TouchDevice.Id, - (int)touchPoint.X, (int)touchPoint.Y); + // Configure the main control properties + HorizontalAlignment = HorizontalAlignment.Stretch; + VerticalAlignment = VerticalAlignment.Stretch; + Focusable = true; + KeyboardNavigation.SetDirectionalNavigation(this, KeyboardNavigationMode.Cycle); + DataContextChanged += CanvasDataContextChanged; - // Ensure control gets focus - DisplayerPane.Focus(); - e.TouchDevice.Capture(DisplayerPane); - - e.Handled = true; - } - - /// - /// Handles the touch move event - /// - private void RenderingCanvas_TouchMove(object sender, TouchEventArgs e) - { - Point touchPoint = e.GetTouchPoint(DisplayerPane).Position; - DispEngine.TouchMove(e.TouchDevice.Id, - (int)touchPoint.X, (int)touchPoint.Y); - e.Handled = true; - } - - /// - /// Handles the touch up event - /// - private void RenderingCanvas_TouchUp(object sender, TouchEventArgs e) - { - DispEngine.TouchUp(e.TouchDevice.Id); - e.Handled = true; - } - #endregion - - #region Window_Events - /// - /// Handles window state changes (maximize, minimize, etc.) - /// - private unsafe void RenderingCanvas_StateChanged(object sender, EventArgs e) - { - switch ((sender as Window).WindowState) - { - case WindowState.Maximized: - DispEngine.IsVisible = true; - break; - case WindowState.Minimized: - DispEngine.IsVisible = false; - break; - case WindowState.Normal: - DispEngine.IsVisible = true; - break; - } - } + // Create and configure the display pane + DisplayerPane = new UserControl(); + DisplayerPane.HorizontalAlignment = HorizontalAlignment.Stretch; + DisplayerPane.VerticalAlignment = VerticalAlignment.Stretch; + DisplayerPane.Focusable = true; + DisplayerPane.IsTabStop = true; - /// - /// Handles data context changes - /// - private unsafe void CanvasDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) - { - DispEngine pre = e.OldValue as DispEngine; - DispEngine cur = e.NewValue as DispEngine; + // Connect event handlers for user input and window events + DisplayerPane.SizeChanged += RenderingCanvas_SizeChanged; + DisplayerPane.MouseMove += RenderingCanvas_MouseMove; + DisplayerPane.MouseDown += RenderingCanvas_MouseDown; + DisplayerPane.MouseUp += RenderingCanvas_MouseUp; + DisplayerPane.MouseWheel += RenderingCanvas_MouseWheel; + DisplayerPane.KeyDown += RenderingCanvas_KeyDown; + DisplayerPane.KeyUp += RenderingCanvas_KeyUp; + DisplayerPane.Loaded += RenderingCanvas_Loaded; + DisplayerPane.Unloaded += RenderingCanvas_Unloaded; + DisplayerPane.IsVisibleChanged += DisplayerPane_IsVisibleChanged; - //child's binding event is triggered after IsVisible event and Load event. - if (pre != null) //this section will never occur if the datacontext not set twice. - { - pre.Terminate(); - pre.ImageRequestAfterBufferSwapped -= RenderingCanvas_BufferSwapped; - } - if (cur != null) - { - cur.ImageRequestAfterBufferSwapped += RenderingCanvas_BufferSwapped; - cur.Start((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height); + // Add touch event handlers + DisplayerPane.TouchDown += RenderingCanvas_TouchDown; + DisplayerPane.TouchMove += RenderingCanvas_TouchMove; + DisplayerPane.TouchUp += RenderingCanvas_TouchUp; - cur.IsVisible = IsVisible; - } - } - - /// - /// Reference to the current window containing this control - /// - private Window currentWindow; - - /// - /// Gets or sets the current window, connecting or disconnecting state change events - /// - Window CurrentWindow - { - get => currentWindow; set - { - if (currentWindow != null) - currentWindow.StateChanged -= RenderingCanvas_StateChanged; - currentWindow = value; - if (currentWindow != null) - currentWindow.StateChanged += RenderingCanvas_StateChanged; - } - } - - /// - /// Handles the loaded event - /// - private unsafe void RenderingCanvas_Loaded(object sender, RoutedEventArgs e) - { - // Get the window containing this control - CurrentWindow = Window.GetWindow(this); - - // Set up DispEngine rendering - DispEngine.ImageRequestAfterBufferSwapped -= RenderingCanvas_BufferSwapped; - DispEngine.ImageRequestAfterBufferSwapped += RenderingCanvas_BufferSwapped; - DispEngine.Start((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height); - DispEngine.IsVisible = IsVisible; - } - - /// - /// Handles the unloaded event - /// - private unsafe void RenderingCanvas_Unloaded(object sender, RoutedEventArgs e) - { - DispEngine.IsVisible = IsVisible; - DispEngine.ImageRequestAfterBufferSwapped -= RenderingCanvas_BufferSwapped; - CurrentWindow = null; - } - #endregion + // Enable touch support + this.IsManipulationEnabled = true; - #region DispEngine_Rendering - /// - /// Handles the buffer swapped event from DispEngine - /// - private unsafe void RenderingCanvas_BufferSwapped(byte* data, int w, int h) - { - if (data == null) - return; + // Initialize power management + InitializePowerManagement(); - Span bgra = new Span(data, w * h * 4); + // Add the display pane to this control's content + Content = DisplayerPane; + } + #endregion - // Copy pixel data from DispEngine - int n = w * h * 4; - byte[] arr = new byte[n]; - for (int i = 0; i < n; i++) - arr[i] = data[i]; - - // Update UI on the UI thread - DisplayerPane.Dispatcher.InvokeAsync(() => - { - BitmapSource bitmap = BitmapSource.Create(w, h, 1, 1, PixelFormats.Bgra32, null, arr, w * 4); - DisplayerPane.Background = new ImageBrush(bitmap); - }); - } - - /// - /// Handles the size changed event - /// - private void RenderingCanvas_SizeChanged(object sender, SizeChangedEventArgs e) - { - // Notify DispEngine of size changes - DispEngine.Resize((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height); - } - - /// - /// Handles visibility changes - /// - private unsafe void DisplayerPane_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) - { - // Update visibility state in DispEngine - DispEngine.IsVisible = IsVisible; - } - #endregion - - #region Keyboard_Events - /// - /// Convert WPF Key to W3C KeyboardEvent.key string. - /// - static string WpfKeyToW3C(Key key) => key switch - { - Key.Home => "Home", - Key.End => "End", - Key.PageUp => "PageUp", - Key.PageDown => "PageDown", - Key.Left => "ArrowLeft", - Key.Right => "ArrowRight", - Key.Up => "ArrowUp", - Key.Down => "ArrowDown", - Key.LeftShift or Key.RightShift => "Shift", - Key.LeftCtrl or Key.RightCtrl => "Control", - Key.LeftAlt or Key.RightAlt => "Alt", - Key.Return => "Enter", - Key.Escape => "Escape", - Key.Back => "Backspace", - Key.Tab => "Tab", - Key.Delete => "Delete", - Key.Insert => "Insert", - Key.Space => " ", - Key.F1 => "F1", - Key.F2 => "F2", - Key.F3 => "F3", - Key.F4 => "F4", - Key.F5 => "F5", - Key.F6 => "F6", - Key.F7 => "F7", - Key.F8 => "F8", - Key.F9 => "F9", - Key.F10 => "F10", - Key.F11 => "F11", - Key.F12 => "F12", - >= Key.A and <= Key.Z => ((char)('a' + (key - Key.A))).ToString(), - >= Key.D0 and <= Key.D9 => ((char)('0' + (key - Key.D0))).ToString(), - _ => "Unidentified" - }; + #region Touch_Events + /// + /// Handles the touch down event + /// + private void RenderingCanvas_TouchDown(object sender, TouchEventArgs e) + { + // Add touch point to dictionary + Point touchPoint = e.GetTouchPoint(DisplayerPane).Position; + DispEngine.TouchDown(e.TouchDevice.Id, + (int)touchPoint.X, (int)touchPoint.Y); - /// - /// Handles the key up event - /// - private void RenderingCanvas_KeyUp(object sender, KeyEventArgs e) - { - DispEngine.KeyUp(WpfKeyToW3C(e.Key)); - } - - /// - /// Handles the key down event - /// - private void RenderingCanvas_KeyDown(object sender, KeyEventArgs e) - { - string key = WpfKeyToW3C(e.Key); - DispEngine.KeyDown(key); + // Ensure control gets focus + DisplayerPane.Focus(); + e.TouchDevice.Capture(DisplayerPane); - DispEngine.KeyDownTransform(key, new key_table__transform_view_by_key_pressing_t() - { - HOME = "Home", - PAGE_UP = "PageUp", - PAGE_DOWN = "PageDown", - F1 = "F1", - F2 = "F2", - F3 = "F3", - F4 = "F4", - SHIFT = "Shift", - ARROW_LEFT = "ArrowLeft", - ARROW_RIGHT = "ArrowRight", - ARROW_DOWN = "ArrowDown", - ARROW_UP = "ArrowUp" - }); - } - #endregion - - #region Mouse_Events - /// - /// Helper method to get mouse button mask - /// - internal static HiMouseButtonMask GetMouseButtonMask(MouseDevice device) - { - HiMouseButtonMask mouseButtonMask = 0; - mouseButtonMask.SetLeftPressed(device.LeftButton == MouseButtonState.Pressed); - mouseButtonMask.SetMiddlePressed(device.MiddleButton == MouseButtonState.Pressed); - mouseButtonMask.SetRightPressed(device.RightButton == MouseButtonState.Pressed); - mouseButtonMask.SetXButton1Pressed(device.XButton1 == MouseButtonState.Pressed); - mouseButtonMask.SetXButton2Pressed(device.XButton2 == MouseButtonState.Pressed); - return mouseButtonMask; - } + e.Handled = true; + } - /// - /// Handles the mouse wheel event - /// - private void RenderingCanvas_MouseWheel(object sender, MouseWheelEventArgs e) - { - // Handle mouse wheel for zoom operations - DispEngine.MouseWheel(0, e.Delta / 120); - DispEngine.MouseWheelTransform(0, e.Delta / 120); - } - - /// - /// Handles the mouse up event - /// - private void RenderingCanvas_MouseUp(object sender, MouseButtonEventArgs e) - { - // Handle mouse button release - DispEngine.MouseButtonUp((long)e.ChangedButton); - (sender as UIElement)?.ReleaseMouseCapture(); - } - - /// - /// Handles the mouse down event - /// - private void RenderingCanvas_MouseDown(object sender, MouseButtonEventArgs e) - { - // Handle mouse button press - DispEngine.MouseButtonDown((long)e.ChangedButton); - DisplayerPane.Focus(); - (sender as UIElement)?.CaptureMouse(); - } - - /// - /// Handles the mouse move event - /// - private void RenderingCanvas_MouseMove(object sender, MouseEventArgs e) - { - // Update mouse position and handle drag transforms - Point p = e.GetPosition(DisplayerPane); - DispEngine.MouseMove((int)p.X, (int)p.Y); - DispEngine.MouseDragTransform((int)p.X, (int)p.Y, - new mouse_button_table__transform_view_by_mouse_drag_t() - { - LEFT_BUTTON = (long)MouseButton.Left, - RIGHT_BUTTON = (long)MouseButton.Right - }); - } - #endregion - - #region Cleanup - /// - /// Flag to track disposed state - /// - private bool disposedValue; - - /// - /// Disposes managed resources - /// - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - // Unsubscribe from power events - SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; - - // Dispose the DispEngine to free resources - DispEngine.Dispose(); - } - disposedValue = true; - } - } - - /// - /// Public dispose method to free resources - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - #endregion - } - #endregion + /// + /// Handles the touch move event + /// + private void RenderingCanvas_TouchMove(object sender, TouchEventArgs e) + { + Point touchPoint = e.GetTouchPoint(DisplayerPane).Position; + DispEngine.TouchMove(e.TouchDevice.Id, + (int)touchPoint.X, (int)touchPoint.Y); + e.Handled = true; + } + + /// + /// Handles the touch up event + /// + private void RenderingCanvas_TouchUp(object sender, TouchEventArgs e) + { + DispEngine.TouchUp(e.TouchDevice.Id); + e.Handled = true; + } + #endregion + + #region Window_Events + /// + /// Handles window state changes (maximize, minimize, etc.) + /// + private unsafe void RenderingCanvas_StateChanged(object sender, EventArgs e) + { + switch ((sender as Window).WindowState) + { + case WindowState.Maximized: + DispEngine.IsVisible = true; + break; + case WindowState.Minimized: + DispEngine.IsVisible = false; + break; + case WindowState.Normal: + DispEngine.IsVisible = true; + break; + } + } + + /// + /// Handles data context changes + /// + private unsafe void CanvasDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + DispEngine pre = e.OldValue as DispEngine; + DispEngine cur = e.NewValue as DispEngine; + + //child's binding event is triggered after IsVisible event and Load event. + if (pre != null) //this section will never occur if the datacontext not set twice. + { + pre.Terminate(); + pre.ImageRequestAfterBufferSwapped -= RenderingCanvas_BufferSwapped; + } + if (cur != null) + { + cur.ImageRequestAfterBufferSwapped += RenderingCanvas_BufferSwapped; + cur.Start((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height); + + cur.IsVisible = IsVisible; + } + } + + /// + /// Reference to the current window containing this control + /// + private Window currentWindow; + + /// + /// Gets or sets the current window, connecting or disconnecting state change events + /// + Window CurrentWindow + { + get => currentWindow; set + { + if (currentWindow != null) + currentWindow.StateChanged -= RenderingCanvas_StateChanged; + currentWindow = value; + if (currentWindow != null) + currentWindow.StateChanged += RenderingCanvas_StateChanged; + } + } + + /// + /// Handles the loaded event + /// + private unsafe void RenderingCanvas_Loaded(object sender, RoutedEventArgs e) + { + // Get the window containing this control + CurrentWindow = Window.GetWindow(this); + + // Set up DispEngine rendering + DispEngine.ImageRequestAfterBufferSwapped -= RenderingCanvas_BufferSwapped; + DispEngine.ImageRequestAfterBufferSwapped += RenderingCanvas_BufferSwapped; + DispEngine.Start((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height); + DispEngine.IsVisible = IsVisible; + } + + /// + /// Handles the unloaded event + /// + private unsafe void RenderingCanvas_Unloaded(object sender, RoutedEventArgs e) + { + DispEngine.IsVisible = IsVisible; + DispEngine.ImageRequestAfterBufferSwapped -= RenderingCanvas_BufferSwapped; + CurrentWindow = null; + } + #endregion + + #region DispEngine_Rendering + /// + /// Handles the buffer swapped event from DispEngine + /// + private unsafe void RenderingCanvas_BufferSwapped(byte* data, int w, int h) + { + if (data == null) + return; + + Span bgra = new Span(data, w * h * 4); + + // Copy pixel data from DispEngine + int n = w * h * 4; + byte[] arr = new byte[n]; + for (int i = 0; i < n; i++) + arr[i] = data[i]; + + // Update UI on the UI thread + DisplayerPane.Dispatcher.InvokeAsync(() => + { + BitmapSource bitmap = BitmapSource.Create(w, h, 1, 1, PixelFormats.Bgra32, null, arr, w * 4); + DisplayerPane.Background = new ImageBrush(bitmap); + }); + } + + /// + /// Handles the size changed event + /// + private void RenderingCanvas_SizeChanged(object sender, SizeChangedEventArgs e) + { + // Notify DispEngine of size changes + DispEngine.Resize((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height); + } + + /// + /// Handles visibility changes + /// + private unsafe void DisplayerPane_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) + { + // Update visibility state in DispEngine + DispEngine.IsVisible = IsVisible; + } + #endregion + + #region Keyboard_Events + /// + /// Convert WPF Key to W3C KeyboardEvent.key string. + /// + static string WpfKeyToW3C(Key key) => key switch + { + Key.Home => "Home", + Key.End => "End", + Key.PageUp => "PageUp", + Key.PageDown => "PageDown", + Key.Left => "ArrowLeft", + Key.Right => "ArrowRight", + Key.Up => "ArrowUp", + Key.Down => "ArrowDown", + Key.LeftShift or Key.RightShift => "Shift", + Key.LeftCtrl or Key.RightCtrl => "Control", + Key.LeftAlt or Key.RightAlt => "Alt", + Key.Return => "Enter", + Key.Escape => "Escape", + Key.Back => "Backspace", + Key.Tab => "Tab", + Key.Delete => "Delete", + Key.Insert => "Insert", + Key.Space => " ", + Key.F1 => "F1", + Key.F2 => "F2", + Key.F3 => "F3", + Key.F4 => "F4", + Key.F5 => "F5", + Key.F6 => "F6", + Key.F7 => "F7", + Key.F8 => "F8", + Key.F9 => "F9", + Key.F10 => "F10", + Key.F11 => "F11", + Key.F12 => "F12", + >= Key.A and <= Key.Z => ((char)('a' + (key - Key.A))).ToString(), + >= Key.D0 and <= Key.D9 => ((char)('0' + (key - Key.D0))).ToString(), + _ => "Unidentified" + }; + + /// + /// Handles the key up event + /// + private void RenderingCanvas_KeyUp(object sender, KeyEventArgs e) + { + DispEngine.KeyUp(WpfKeyToW3C(e.Key)); + } + + /// + /// Handles the key down event + /// + private void RenderingCanvas_KeyDown(object sender, KeyEventArgs e) + { + string key = WpfKeyToW3C(e.Key); + DispEngine.KeyDown(key); + + DispEngine.KeyDownTransform(key, new key_table__transform_view_by_key_pressing_t() + { + HOME = "Home", + PAGE_UP = "PageUp", + PAGE_DOWN = "PageDown", + F1 = "F1", + F2 = "F2", + F3 = "F3", + F4 = "F4", + SHIFT = "Shift", + ARROW_LEFT = "ArrowLeft", + ARROW_RIGHT = "ArrowRight", + ARROW_DOWN = "ArrowDown", + ARROW_UP = "ArrowUp" + }); + } + #endregion + + #region Mouse_Events + /// + /// Helper method to get mouse button mask + /// + internal static HiMouseButtonMask GetMouseButtonMask(MouseDevice device) + { + HiMouseButtonMask mouseButtonMask = 0; + mouseButtonMask.SetLeftPressed(device.LeftButton == MouseButtonState.Pressed); + mouseButtonMask.SetMiddlePressed(device.MiddleButton == MouseButtonState.Pressed); + mouseButtonMask.SetRightPressed(device.RightButton == MouseButtonState.Pressed); + mouseButtonMask.SetXButton1Pressed(device.XButton1 == MouseButtonState.Pressed); + mouseButtonMask.SetXButton2Pressed(device.XButton2 == MouseButtonState.Pressed); + return mouseButtonMask; + } + + /// + /// Handles the mouse wheel event + /// + private void RenderingCanvas_MouseWheel(object sender, MouseWheelEventArgs e) + { + // Handle mouse wheel for zoom operations + DispEngine.MouseWheel(0, e.Delta / 120); + DispEngine.MouseWheelTransform(0, e.Delta / 120); + } + + /// + /// Handles the mouse up event + /// + private void RenderingCanvas_MouseUp(object sender, MouseButtonEventArgs e) + { + // Handle mouse button release + DispEngine.MouseButtonUp((long)e.ChangedButton); + (sender as UIElement)?.ReleaseMouseCapture(); + } + + /// + /// Handles the mouse down event + /// + private void RenderingCanvas_MouseDown(object sender, MouseButtonEventArgs e) + { + // Handle mouse button press + DispEngine.MouseButtonDown((long)e.ChangedButton); + DisplayerPane.Focus(); + (sender as UIElement)?.CaptureMouse(); + } + + /// + /// Handles the mouse move event + /// + private void RenderingCanvas_MouseMove(object sender, MouseEventArgs e) + { + // Update mouse position and handle drag transforms + Point p = e.GetPosition(DisplayerPane); + DispEngine.MouseMove((int)p.X, (int)p.Y); + DispEngine.MouseDragTransform((int)p.X, (int)p.Y, + new mouse_button_table__transform_view_by_mouse_drag_t() + { + LEFT_BUTTON = (long)MouseButton.Left, + RIGHT_BUTTON = (long)MouseButton.Right + }); + } + #endregion + + #region Cleanup + /// + /// Flag to track disposed state + /// + private bool disposedValue; + + /// + /// Disposes managed resources + /// + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + // Unsubscribe from power events + SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; + + // Dispose the DispEngine to free resources + DispEngine.Dispose(); + } + disposedValue = true; + } + } + + /// + /// Public dispose method to free resources + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + #endregion } +#endregion diff --git a/Disp/RenderingWindow.cs b/Disp/RenderingWindow.cs index b13fc6b..0e3d7c9 100644 --- a/Disp/RenderingWindow.cs +++ b/Disp/RenderingWindow.cs @@ -3,53 +3,52 @@ using System; using System.Windows; using System.Windows.Media; -namespace Hi.WpfPlus.Disp -{ - /// - /// Window for 3D rendering. - /// - public class RenderingWindow : Window, IGetDispEngine - { - /// - /// Gets the rendering canvas control used for displaying 3D content. - /// - public RenderingCanvas RenderingCanvas => Content as RenderingCanvas; +namespace Hi.WpfPlus.Disp; - /// - /// Ctor. - /// - public RenderingWindow() - { - Title = nameof(RenderingWindow); - Height = 450; - Width = 800; - StateChanged += RenderingWind_StateChanged; - Content = new RenderingCanvas() - { - BorderThickness = new Thickness(1), - BorderBrush = Brushes.Black - }; - } - private void RenderingWind_StateChanged(object sender, EventArgs e) - { - RenderingCanvas.DispEngine.IsVisible = WindowState != WindowState.Minimized; - } - /// - public DispEngine GetDispEngine() => RenderingCanvas.DispEngine; - /// - /// Gets or sets the current displayable 3D object. - /// When setting a new displayee, the view will be reset to home position if no previous displayee was set. - /// - public IDisplayee Displayee - { - get => GetDispEngine().Displayee; - set - { - var preDisplayee = GetDispEngine().Displayee; - GetDispEngine().Displayee = value; - if (preDisplayee == null) - RenderingCanvas.DispEngine.SetViewToHomeView(); - } - } - } +/// +/// Window for 3D rendering. +/// +public class RenderingWindow : Window, IGetDispEngine +{ + /// + /// Gets the rendering canvas control used for displaying 3D content. + /// + public RenderingCanvas RenderingCanvas => Content as RenderingCanvas; + + /// + /// Ctor. + /// + public RenderingWindow() + { + Title = nameof(RenderingWindow); + Height = 450; + Width = 800; + StateChanged += RenderingWind_StateChanged; + Content = new RenderingCanvas() + { + BorderThickness = new Thickness(1), + BorderBrush = Brushes.Black + }; + } + private void RenderingWind_StateChanged(object sender, EventArgs e) + { + RenderingCanvas.DispEngine.IsVisible = WindowState != WindowState.Minimized; + } + /// + public DispEngine GetDispEngine() => RenderingCanvas.DispEngine; + /// + /// Gets or sets the current displayable 3D object. + /// When setting a new displayee, the view will be reset to home position if no previous displayee was set. + /// + public IDisplayee Displayee + { + get => GetDispEngine().Displayee; + set + { + var preDisplayee = GetDispEngine().Displayee; + GetDispEngine().Displayee = value; + if (preDisplayee == null) + RenderingCanvas.DispEngine.SetViewToHomeView(); + } + } } diff --git a/Disp/WpfDisp.cs b/Disp/WpfDisp.cs new file mode 100644 index 0000000..6a82085 --- /dev/null +++ b/Disp/WpfDisp.cs @@ -0,0 +1,60 @@ +using Google.Protobuf.WellKnownTypes; +using Hi.Disp; +using Hi.Geom; +using System.Collections.Concurrent; +using System.Windows; + +namespace Hi.WpfPlus.Disp; + +/// +/// Registers WPF as the display framework for , +/// supporting multiple windows identified by key. +/// +/// +/// +/// Usage pattern: call to queue display content, +/// then call to start the WPF application and show windows. +/// +/// +/// Each unique key creates a separate . +/// Calling with the same key updates the existing window. +/// +/// +/// +/// // Queue display content (before or after Run) +/// DispFrameUtil.CallDispFrame("Window1", displayee1); +/// DispFrameUtil.CallDispFrame("Window2", displayee2); +/// // Start the WPF application (blocks until all windows are closed) +/// DispFrameWpf.Run(); +/// +/// +/// +public static class WpfDisp +{ + static readonly ConcurrentDictionary KeyToWindowDictionary = new(); + + static WpfDisp() + { + DispFrameUtil.UpdateByDispEngineConfigFunc = ApplyConfig; + } + public static void Init() { } + + static void ApplyConfig(string key, DispEngineConfig config) + { + if (!KeyToWindowDictionary.TryGetValue(key, out var window)) + { + window = new RenderingWindow() { Title = key }; + window.Closed += (s, e) => KeyToWindowDictionary.TryRemove(key, out _); + KeyToWindowDictionary[key] = window; + window.Show(); + } + + var dispEngine = window.GetDispEngine(); + if (config.Displayee != null) + dispEngine.Displayee = config.Displayee; + if (config.SketchView == null) + config.SketchView = config.Displayee?.GetBox3d()?.FrontView; + if (config.SketchView != null) + dispEngine.SketchView = config.SketchView; + } +}