Compare commits

..
69 Commits
Author SHA1 Message Date
iambossandClaude Opus 5 60dea348d2 chore(Hi.WpfPlus): bump VersionBuild to 3.1.160
Carried over from a deploy.py run -- incrementPackageID bumps VersionBuild but
deploy.py only auto-commits HiAPI-docsite-2025.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 20:11:15 +08:00
iambossandClaude Opus 5 85d5515a97 build(Hi.WpfPlus): keep the package line on 3.1 and resume the build counter
The 3.2 line is retracted -- the major version stays fixed for commercial
positioning, so 3.1 remains the development line. Version stamp only; no code
changed on the way out or back.

VersionBuild resumes at 158, where it stood before the detour, instead of
restarting from the 143 the feed serves. Everything between was built locally and
stamped into real assemblies even though deploy.py deliberately withholds the
upload until the API has settled commercially, and re-minting a spent number with
different code is the one failure that yields two Hi.WpfPlus.dll claiming the same
version. Next upload is 3.1.159; the 143 -> 159 gap on the feed is intended.

3.2.1 was packed locally and never uploaded, so nothing outside this machine saw
the 3.2 line at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 14:27:48 +08:00
iambossandClaude Opus 5 c25bd820c3 build(Hi.WpfPlus): start the 3.2 package line and restart VersionBuild
The 3.1 line ends at Hi.WpfPlus 3.1.143 -- tag v3.1.143, part of the aligned
hinc-suite/3.1.175 set. master now produces 3.2 and VersionBuild restarts at 0,
so the first published 3.2 package is 3.2.1: IncrementPackageID.targets bumps
before the build, not after it.

The 3.1.158 stamp this replaces was a local increment from a deploy run whose
upload step is commented out, so it never reached the feed and nothing depends on
it. Fixes for customers pinned to 3.1 come off the support/3.1 branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 18:00:18 +08:00
iambossandClaude Opus 4.8 a5a778fb1c chore(Hi.WpfPlus): bump VersionBuild
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 12:36:47 +08:00
iamboss f9a85f0e33 tune. 2026-07-07 18:52:21 +08:00
iamboss 2097cc6bb3 deploy fixed MRR drift and webservice CylindroidHolder buckle branch update issue. 2026-05-28 20:20:39 +08:00
iamboss 277c87ad3d deploy fixed MRR drift and webservice CylindroidHolder buckle branch update issue. 2026-05-28 19:34:22 +08:00
iambossandClaude Opus 4.7 c1b86be4aa fix(Hi.WpfPlus): drop stale using Google.Protobuf.WellKnownTypes
WpfDispUtil.cs carried a stale `using Google.Protobuf.WellKnownTypes;`
with zero matching symbol usage in the file body — it resolved only
because HiGeom used to re-export Google.Protobuf transitively via its
now-removed Grpc.Tools / Google.Protobuf PackageRefs. CS0246 on
'Google' once that transitive bring-in went away. VersionBuild bump
140→141 rides along for the NuGet republish.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 18:57:12 +08:00
iamboss 24c85e3cde update XML IO pattern. 2026-05-24 13:27:54 +08:00
iamboss c72b1f48c3 rename XFactory.Regs to XFactory.Generators. 2026-05-23 21:09:37 +08:00
iamboss 56bd2cd85f fix SoftNcRunner GM code normalization. 2026-05-16 17:42:12 +08:00
iamboss ee455c013c fix SoftNcRunner GM code normalization. 2026-05-08 22:04:31 +08:00
iamboss 66c927e0b7 fix SoftNcRunner GM code normalization. 2026-05-06 13:12:10 +08:00
iamboss f688e555a8 fix SoftNcRunner GM code normalization. 2026-04-26 07:42:55 +08:00
iamboss 0bc34dfdd5 fix SoftNcRunner GM code normalization. 2026-04-22 19:23:22 +08:00
iamboss 84ee564a2e fix SoftNcRunner GM code normalization. 2026-04-22 16:11:08 +08:00
iamboss 65ede317da applying SoftNcRunner. 2026-04-21 18:40:10 +08:00
iamboss 0208886310 tune. 2026-04-11 12:54:25 +08:00
iamboss 83f0d467d8 chore: update related files 2026-04-08 16:31:07 +08:00
iamboss f974500f05 update ISO coordinate rendering for 3+2axis machine. 2026-04-06 14:58:53 +08:00
iamboss 8dba25293b fix blazor MechBuilderDiv StlFileDiv directory candidates. 2026-03-25 16:33:06 +08:00
iamboss 53f98aa71d fix bug of cubetree init SAT and triangles-tip-cube-corner margin case. 2026-03-25 12:34:47 +08:00
iamboss 95187d7c37 fix bug of G53.1 and remove NcEnv.SetToolHeightCompensationOnFeatureNormal property. 2026-03-19 17:19:00 +08:00
iamboss 7dd5044f19 fix issue of step.csv null reading. 2026-03-16 12:23:45 +08:00
iamboss 84cc69a34f fix bug of flagTexts disappear by Sentence property type. 2026-03-11 11:14:24 +08:00
iamboss 198eb8c0dd fix bug of opt arc circle offset. 2026-03-08 12:30:23 +08:00
iamboss 140e446569 fix rtcp on un-matched tool offset. 2026-03-06 01:16:52 +08:00
iamboss d014fb52f2 fix opt rotation code jumping at pi-cycle bounding. 2026-03-02 20:22:41 +08:00
iamboss 698583fa06 fix G68 mis-behavior. 2026-02-24 23:37:11 +08:00
iamboss 64272c0ada fix postprocess precision. 2026-02-19 21:23:52 +08:00
iamboss bb92237613 fix cubetree init crash. 2026-02-17 15:42:50 +08:00
iamboss 68736b79fb tune. 2026-02-15 16:34:59 +08:00
iamboss c1f3fefdc0 merge frame part of DispUtil to DispFrameUtil. 2026-02-15 16:08:00 +08:00
iamboss 688762ab70 add cubetree defect display. 2026-02-11 04:04:05 +08:00
iamboss 0e0adaa98f update disp_engine_t and panel key code to web-based string instead of int64. 2026-02-10 21:49:46 +08:00
iamboss aa85e54a44 add cubetree defect display. 2026-02-10 01:18:01 +08:00
iamboss aad9fb1a8d add cubetree defect display. 2026-02-10 00:29:57 +08:00
iamboss 65ae47b3a8 add cubetree defect display. 2026-02-09 22:34:36 +08:00
iamboss f577c37dd5 fix obfuscar internal class issue. 2026-02-01 18:50:09 +08:00
iamboss cd21f45b30 tune. 2026-02-01 16:59:03 +08:00
iamboss c586dbbd58 tune. 2026-02-01 16:33:28 +08:00
iamboss 05b2ff6c59 fix siemens trafoof plain rotation. 2026-01-31 17:08:06 +08:00
iamboss dfc6ce0626 fix sqlite ubuntu login issue. 2026-01-31 10:44:34 +08:00
iamboss eadc38cfda fix siemens coordinate transform failed on succesive file running. 2026-01-30 20:49:45 +08:00
iamboss a9ecd1a4b8 fix relief face collision from floating issue. 2026-01-26 03:46:04 +08:00
iamboss 968c6fa1ac fix 2026-01-26 03:33:30 +08:00
iamboss 9ed8b5d60a apply 2026-01-26 02:43:48 +08:00
iamboss 5e22abcc22 build 2026-01-24 18:05:27 +08:00
iamboss c0f6dc90a1 add 2026-01-22 02:20:29 +08:00
iamboss b341f75af6 set 2026-01-17 15:58:44 +08:00
iamboss db847bd2d8 fix 2026-01-15 23:27:51 +08:00
iamboss 27511a1893 add 2026-01-08 20:45:11 +08:00
iamboss 425fb4d4a9 tune. 2026-01-01 03:59:22 +08:00
iamboss 8194797989 adjust 2025-12-30 15:36:43 +08:00
iamboss 0086973818 adjust 2025-12-29 11:48:51 +08:00
iamboss f247ec037a fix 2025-12-27 11:54:44 +08:00
iamboss 76308d1ec6 seperate 2025-12-25 16:57:28 +08:00
iamboss 32cc3ec75e add 2025-12-23 13:31:31 +08:00
iamboss 382569196a add 2025-12-22 23:29:13 +08:00
iamboss 3b54a03b13 split runtime functions from MachiningProject to LocalProjectService. 2025-12-21 13:44:45 +08:00
iamboss 55f61d0903 spliting runtime functions from MachiningProject to LocalProjectService. 2025-12-20 00:01:47 +08:00
iamboss b9dc44f127 cancel 2025-12-18 09:44:23 +08:00
iamboss 7101573e91 cancel 2025-12-14 01:22:28 +08:00
iamboss bac0d9fa00 cancel 2025-12-14 01:11:59 +08:00
iamboss fd93271f87 remove 2025-12-13 15:08:30 +08:00
iamboss ca8236836c add 2025-12-12 13:10:36 +08:00
iamboss 86f4a7ad56 fix 2025-12-11 12:41:49 +08:00
iamboss 83397d7095 add 2025-12-10 16:31:29 +08:00
iamboss 6b8925de51 adjusting 2025-12-08 23:02:51 +08:00
6 changed files with 601 additions and 465 deletions
+443 -408
View File
@@ -1,4 +1,4 @@
using Hi.Disp;
using Hi.Disp;
using Hi.Native;
using Hi.PanelModels;
using System;
@@ -11,428 +11,463 @@ using System.Collections.Generic;
using Hi.Geom;
using Microsoft.Win32; // Add this for SystemEvents
namespace Hi.Wpf.Disp
namespace Hi.WpfPlus.Disp;
#region WPF Rendering Canvas
/// <summary>
/// Provides a WPF rendering canvas for 3D visualization of HiAPI components.
/// Handles user interactions, rendering, and integration with the DispEngine system.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public class RenderingCanvas : UserControl, IDisposable
{
#region WPF Rendering Canvas
/// <summary>
/// Provides a WPF rendering canvas for 3D visualization of HiAPI components.
/// Handles user interactions, rendering, and integration with the DispEngine system.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public class RenderingCanvas : UserControl, IDisposable
{
#region Core_Properties
/// <summary>
/// The DispEngine instance that handles rendering and user interactions
/// </summary>
public DispEngine DispEngine { get; } = new DispEngine();
/// <summary>
/// Internal container for rendering content
/// </summary>
private UserControl DisplayerPane { get; }
/// <summary>
/// Dictionary to store touch point information
/// </summary>
private Dictionary<int, Point> TouchingPointsMap { get; } = new Dictionary<int, Point>();
/// <summary>
/// Dictionary to store previous positions of touch points
/// </summary>
private Dictionary<int, Point> PreviousTouchingPointsMap { get; } = new Dictionary<int, Point>();
#endregion
#region Power_Management
/// <summary>
/// Handles system power mode changes
/// </summary>
private void InitializePowerManagement()
{
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
}
#region Core_Properties
/// <summary>
/// The DispEngine instance that handles rendering and user interactions
/// </summary>
public DispEngine DispEngine { get; } = new DispEngine();
/// <summary>
/// Handles power mode change events
/// </summary>
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
/// <summary>
/// Initializes a new instance of the RenderingCanvas
/// </summary>
public RenderingCanvas()
{
DispEngine.BackgroundColor = new Vec3d(0.1, 0.1, 0.5);
DispEngine.BackgroundOpacity = 0.1;
/// <summary>
/// Internal container for rendering content
/// </summary>
private UserControl DisplayerPane { get; }
// Configure the main control properties
HorizontalAlignment = HorizontalAlignment.Stretch;
VerticalAlignment = VerticalAlignment.Stretch;
Focusable = true;
KeyboardNavigation.SetDirectionalNavigation(this, KeyboardNavigationMode.Cycle);
DataContextChanged += CanvasDataContextChanged;
/// <summary>
/// Dictionary to store touch point information
/// </summary>
private Dictionary<int, Point> TouchingPointsMap { get; } = new Dictionary<int, Point>();
// Create and configure the display pane
DisplayerPane = new UserControl();
DisplayerPane.HorizontalAlignment = HorizontalAlignment.Stretch;
DisplayerPane.VerticalAlignment = VerticalAlignment.Stretch;
DisplayerPane.Focusable = true;
DisplayerPane.IsTabStop = true;
/// <summary>
/// Dictionary to store previous positions of touch points
/// </summary>
private Dictionary<int, Point> PreviousTouchingPointsMap { get; } = new Dictionary<int, Point>();
#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
/// <summary>
/// Handles system power mode changes
/// </summary>
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;
/// <summary>
/// Handles power mode change events
/// </summary>
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
/// <summary>
/// Initializes a new instance of the RenderingCanvas
/// </summary>
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
/// <summary>
/// Handles the touch down event
/// </summary>
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;
}
/// <summary>
/// Handles the touch move event
/// </summary>
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;
}
/// <summary>
/// Handles the touch up event
/// </summary>
private void RenderingCanvas_TouchUp(object sender, TouchEventArgs e)
{
DispEngine.TouchUp(e.TouchDevice.Id);
e.Handled = true;
}
#endregion
#region Window_Events
/// <summary>
/// Handles window state changes (maximize, minimize, etc.)
/// </summary>
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;
/// <summary>
/// Handles data context changes
/// </summary>
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;
}
}
/// <summary>
/// Reference to the current window containing this control
/// </summary>
private Window currentWindow;
/// <summary>
/// Gets or sets the current window, connecting or disconnecting state change events
/// </summary>
Window CurrentWindow
{
get => currentWindow; set
{
if (currentWindow != null)
currentWindow.StateChanged -= RenderingCanvas_StateChanged;
currentWindow = value;
if (currentWindow != null)
currentWindow.StateChanged += RenderingCanvas_StateChanged;
}
}
/// <summary>
/// Handles the loaded event
/// </summary>
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;
}
/// <summary>
/// Handles the unloaded event
/// </summary>
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
/// <summary>
/// Handles the buffer swapped event from DispEngine
/// </summary>
private unsafe void RenderingCanvas_BufferSwapped(byte* data, int w, int h)
{
if (data == null)
return;
// Initialize power management
InitializePowerManagement();
Span<byte> bgra = new Span<byte>(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);
});
}
/// <summary>
/// Handles the size changed event
/// </summary>
private void RenderingCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
{
// Notify DispEngine of size changes
DispEngine.Resize((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height);
}
/// <summary>
/// Handles visibility changes
/// </summary>
private unsafe void DisplayerPane_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Update visibility state in DispEngine
DispEngine.IsVisible = IsVisible;
}
#endregion
#region Keyboard_Events
/// <summary>
/// Handles the key up event
/// </summary>
private void RenderingCanvas_KeyUp(object sender, KeyEventArgs e)
{
DispEngine.KeyUp((long)e.Key);
}
/// <summary>
/// Handles the key down event
/// </summary>
private void RenderingCanvas_KeyDown(object sender, KeyEventArgs e)
{
DispEngine.KeyDown((long)e.Key);
#region Touch_Events
/// <summary>
/// Handles the touch down event
/// </summary>
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);
// Map specific keys for view transformation
long key = (long)e.Key;
if (key == (long)Key.RightShift)
key = (long)Key.LeftShift;
DispEngine.KeyDownTransform(key, new key_table__transform_view_by_key_pressing_t()
{
HOME = (long)Key.Home,
PAGE_UP = (long)Key.PageUp,
PAGE_DOWN = (long)Key.PageDown,
F1 = (long)Key.F1,
F2 = (long)Key.F2,
F3 = (long)Key.F3,
F4 = (long)Key.F4,
SHIFT = (long)Key.LeftShift,
ARROW_LEFT = (long)Key.Left,
ARROW_RIGHT = (long)Key.Right,
ARROW_DOWN = (long)Key.Down,
ARROW_UP = (long)Key.Up
});
}
#endregion
#region Mouse_Events
/// <summary>
/// Helper method to get mouse button mask
/// </summary>
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;
}
// Ensure control gets focus
DisplayerPane.Focus();
e.TouchDevice.Capture(DisplayerPane);
/// <summary>
/// Handles the mouse wheel event
/// </summary>
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);
}
/// <summary>
/// Handles the mouse up event
/// </summary>
private void RenderingCanvas_MouseUp(object sender, MouseButtonEventArgs e)
{
// Handle mouse button release
DispEngine.MouseButtonUp((long)e.ChangedButton);
(sender as UIElement)?.ReleaseMouseCapture();
}
/// <summary>
/// Handles the mouse down event
/// </summary>
private void RenderingCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
// Handle mouse button press
DispEngine.MouseButtonDown((long)e.ChangedButton);
DisplayerPane.Focus();
(sender as UIElement)?.CaptureMouse();
}
/// <summary>
/// Handles the mouse move event
/// </summary>
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
/// <summary>
/// Flag to track disposed state
/// </summary>
private bool disposedValue;
/// <summary>
/// Disposes managed resources
/// </summary>
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;
}
}
/// <summary>
/// Public dispose method to free resources
/// </summary>
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}
#endregion
e.Handled = true;
}
/// <summary>
/// Handles the touch move event
/// </summary>
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;
}
/// <summary>
/// Handles the touch up event
/// </summary>
private void RenderingCanvas_TouchUp(object sender, TouchEventArgs e)
{
DispEngine.TouchUp(e.TouchDevice.Id);
e.Handled = true;
}
#endregion
#region Window_Events
/// <summary>
/// Handles window state changes (maximize, minimize, etc.)
/// </summary>
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;
}
}
/// <summary>
/// Handles data context changes
/// </summary>
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;
}
}
/// <summary>
/// Reference to the current window containing this control
/// </summary>
private Window currentWindow;
/// <summary>
/// Gets or sets the current window, connecting or disconnecting state change events
/// </summary>
Window CurrentWindow
{
get => currentWindow; set
{
if (currentWindow != null)
currentWindow.StateChanged -= RenderingCanvas_StateChanged;
currentWindow = value;
if (currentWindow != null)
currentWindow.StateChanged += RenderingCanvas_StateChanged;
}
}
/// <summary>
/// Handles the loaded event
/// </summary>
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;
}
/// <summary>
/// Handles the unloaded event
/// </summary>
private unsafe void RenderingCanvas_Unloaded(object sender, RoutedEventArgs e)
{
DispEngine.IsVisible = IsVisible;
DispEngine.ImageRequestAfterBufferSwapped -= RenderingCanvas_BufferSwapped;
CurrentWindow = null;
}
#endregion
#region DispEngine_Rendering
/// <summary>
/// Handles the buffer swapped event from DispEngine
/// </summary>
private unsafe void RenderingCanvas_BufferSwapped(byte* data, int w, int h)
{
if (data == null)
return;
Span<byte> bgra = new Span<byte>(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);
});
}
/// <summary>
/// Handles the size changed event
/// </summary>
private void RenderingCanvas_SizeChanged(object sender, SizeChangedEventArgs e)
{
// Notify DispEngine of size changes
DispEngine.Resize((int)DisplayerPane.RenderSize.Width, (int)DisplayerPane.RenderSize.Height);
}
/// <summary>
/// Handles visibility changes
/// </summary>
private unsafe void DisplayerPane_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
// Update visibility state in DispEngine
DispEngine.IsVisible = IsVisible;
}
#endregion
#region Keyboard_Events
/// <summary>
/// Convert WPF Key to W3C KeyboardEvent.key string.
/// </summary>
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"
};
/// <summary>
/// Handles the key up event
/// </summary>
private void RenderingCanvas_KeyUp(object sender, KeyEventArgs e)
{
DispEngine.KeyUp(WpfKeyToW3C(e.Key));
}
/// <summary>
/// Handles the key down event
/// </summary>
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
/// <summary>
/// Helper method to get mouse button mask
/// </summary>
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;
}
/// <summary>
/// Handles the mouse wheel event
/// </summary>
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);
}
/// <summary>
/// Handles the mouse up event
/// </summary>
private void RenderingCanvas_MouseUp(object sender, MouseButtonEventArgs e)
{
// Handle mouse button release
DispEngine.MouseButtonUp((long)e.ChangedButton);
(sender as UIElement)?.ReleaseMouseCapture();
}
/// <summary>
/// Handles the mouse down event
/// </summary>
private void RenderingCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
// Handle mouse button press
DispEngine.MouseButtonDown((long)e.ChangedButton);
DisplayerPane.Focus();
(sender as UIElement)?.CaptureMouse();
}
/// <summary>
/// Handles the mouse move event
/// </summary>
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
/// <summary>
/// Flag to track disposed state
/// </summary>
private bool disposedValue;
/// <summary>
/// Disposes managed resources
/// </summary>
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;
}
}
/// <summary>
/// Public dispose method to free resources
/// </summary>
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}
#endregion
+47 -49
View File
@@ -1,56 +1,54 @@
using Hi.Disp;
using Hi.Licenses;
using System;
using System.Windows;
using System.Windows.Media;
namespace Hi.Wpf.Disp
{
/// <summary>
/// Window for 3D rendering.
/// </summary>
public class RenderingWindow : Window, IGetDispEngine
{
/// <summary>
/// Gets the rendering canvas control used for displaying 3D content.
/// </summary>
public RenderingCanvas RenderingCanvas => Content as RenderingCanvas;
namespace Hi.WpfPlus.Disp;
/// <summary>
/// Ctor.
/// </summary>
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;
}
/// <inheritdoc/>
public DispEngine GetDispEngine() => RenderingCanvas.DispEngine;
/// <summary>
/// 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.
/// </summary>
public IDisplayee Displayee
{
get => GetDispEngine().Displayee;
set
{
var preDisplayee = GetDispEngine().Displayee;
GetDispEngine().Displayee = value;
if (preDisplayee == null)
RenderingCanvas.DispEngine.SetViewToHomeView();
}
}
}
/// <summary>
/// Window for 3D rendering.
/// </summary>
public class RenderingWindow : Window, IGetDispEngine
{
/// <summary>
/// Gets the rendering canvas control used for displaying 3D content.
/// </summary>
public RenderingCanvas RenderingCanvas => Content as RenderingCanvas;
/// <summary>
/// Ctor.
/// </summary>
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;
}
/// <inheritdoc/>
public DispEngine GetDispEngine() => RenderingCanvas.DispEngine;
/// <summary>
/// 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.
/// </summary>
public IDisplayee Displayee
{
get => GetDispEngine().Displayee;
set
{
var preDisplayee = GetDispEngine().Displayee;
GetDispEngine().Displayee = value;
if (preDisplayee == null)
RenderingCanvas.DispEngine.SetViewToHomeView();
}
}
}
+100
View File
@@ -0,0 +1,100 @@
using Hi.Disp;
using Hi.Geom;
using Hi.Licenses;
using System.Collections.Concurrent;
using System.Windows;
namespace Hi.WpfPlus.Disp;
/// <summary>
/// Registers WPF as the display framework for <see cref="DispFrameUtil"/>,
/// supporting multiple windows identified by key.
/// </summary>
/// <remarks>
/// <para>
/// Usage pattern: call <see cref="DispFrameUtil.Call"/> to queue display content,
/// then call <see cref="RunApplication()"/> to start the WPF application and show windows.
/// </para>
/// <para>
/// Each unique key creates a separate <see cref="RenderingWindow"/>.
/// Calling <see cref="DispFrameUtil.Call"/> with the same key updates the existing window.
/// </para>
/// <example>
/// <code>
/// // 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();
/// </code>
/// </example>
/// </remarks>
public static class WpfDispUtil
{
static readonly ConcurrentDictionary<string, RenderingWindow> KeyToWindowDictionary = new();
static WpfDispUtil()
{
DispFrameUtil.UpdateByDispEngineConfigFunc = ApplyConfig;
}
/// <summary>
/// Initializes the display engine.
/// </summary>
public static void Init() {
DispEngine.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;
}
/// <summary>
/// Configures the display engine with the specified displayees for the given title.
/// </summary>
/// <param name="title">The title/key to identify the display window.</param>
/// <param name="displayees">The displayees to be configured.</param>
/// <returns>The display engine configuration.</returns>
public static DispEngineConfig Call(
string title, params IDisplayee[] displayees)=>
DispFrameUtil.Call(title, displayees);
/// <summary>
/// Configures the display engine with the specified displayees and starts the WPF application.
/// </summary>
/// <param name="title">The title/key to identify the display window.</param>
/// <param name="displayees">The displayees to be configured.</param>
public static void RunApplication(string title, params IDisplayee[] displayees)
{
DispFrameUtil.Call(title, displayees);
RunApplication();
}
/// <summary>
/// Starts the WPF application and shows all configured windows.
/// Blocks until all windows are closed.
/// </summary>
public static void RunApplication()
{
Application app = new Application()
{
ShutdownMode = ShutdownMode.OnLastWindowClose
};
//app.Exit += (o, e) =>
//{
// License.LogOutAll();
//};
app.Run();
}
}
+5 -4
View File
@@ -3,17 +3,18 @@
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0-windows</TargetFramework>
<TargetFramework>net10.0-windows</TargetFramework>
<Platforms>x64</Platforms>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AssemblyName>Hi.Wpf</AssemblyName>
<AssemblyName>Hi.WpfPlus</AssemblyName>
<RootNamespace>$(AssemblyName)</RootNamespace>
<Description>
HiAPI wpf canvas.
</Description>
<Configurations>Debug;Release</Configurations>
<VersionBuild>69</VersionBuild>
<VersionBuild>160</VersionBuild>
<VersionPrefix>3.1.$(VersionBuild)</VersionPrefix>
<PackageId>Hi.WpfPlus</PackageId>
<PackageTags>HiAPI</PackageTags>
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
<Product>$(AssemblyName)</Product>
@@ -21,7 +22,7 @@
<Copyright>Copyright 2025 Tech Coordinate Co., Ltd.</Copyright>
<Company>Tech Coordinate Co., Ltd.</Company>
<Authors>Tech Coordinate Co., Ltd.</Authors>
<StartupObject>Hi.Wpf.Program</StartupObject>
<StartupObject>Hi.WpfPlus.Program</StartupObject>
</PropertyGroup>
<Import Project="..\acc\common_info.props" />
<ItemGroup Condition="'$(Configuration)'=='Debug'">
+1 -1
View File
@@ -2,7 +2,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hi.Wpf", "Hi.Wpf.csproj", "{91AB5E3A-A30A-6142-C83E-5EE726B5FA0C}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hi.WpfPlus", "Hi.WpfPlus.csproj", "{91AB5E3A-A30A-6142-C83E-5EE726B5FA0C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+5 -3
View File
@@ -2,18 +2,20 @@
using Hi.Disp.Flag;
using Hi.Geom;
using Hi.Licenses;
using Hi.Wpf.Disp;
using Hi.WpfPlus.Disp;
using Microsoft.Extensions.Logging;
using System;
using System.Windows;
namespace Hi.Wpf
namespace Hi.WpfPlus
{
static class Program
{
[STAThread]
public static int Main(string[] args)
{
License.LogInAll();
License.AbortMessageAction += Console.WriteLine;
License.LogInAll();
DispEngine.Init();
Application app = new Application