2025-10-13 00:24:11 +08:00

599 lines
23 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vec3dControl Component | HiAPI-C# 2025 </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Vec3dControl Component | HiAPI-C# 2025 ">
<link rel="icon" href="../../../../../img/HiAPI.favicon.ico">
<link rel="stylesheet" href="../../../../../public/docfx.min.css">
<link rel="stylesheet" href="../../../../../public/main.css">
<meta name="docfx:navrel" content="../../../../../toc.html">
<meta name="docfx:tocrel" content="../../../toc.html">
<meta name="docfx:rel" content="../../../../../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../../../../../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../../../../../index.html">
<img id="logo" class="svg" src="../../../../../img/HiAPI.logo.png" alt="">
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="vec3dcontrol-component">Vec3dControl Component</h1>
<p>Vec3dControl is a user control for <a class="xref" href="../../../../../api/Hi.Geom.Vec3d.html">Vec3d</a> and display.</p>
<h2 id="main-features">Main Features</h2>
<ol>
<li><p>Dual Input Modes</p>
<ul>
<li>Standard Mode: Input X, Y, Z components separately</li>
<li>Text Mode: Input complete vector in text format</li>
</ul>
</li>
<li><p>Vector Normalization</p>
<ul>
<li>Normalization button (optional)</li>
<li>Button visibility controlled by <code>ShowNormalizeButton</code> property</li>
</ul>
</li>
</ol>
<h2 id="refer-sample-code">Refer Sample Code</h2>
<pre><code class="lang-csharp" name="SampleCode-xaml">&lt;UserControl x:Class=&quot;HiNC_2025_win_desktop.Geom.Vec3dControl&quot;
xmlns=&quot;http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
xmlns:x=&quot;http://schemas.microsoft.com/winfx/2006/xaml&quot;
xmlns:mc=&quot;http://schemas.openxmlformats.org/markup-compatibility/2006&quot;
xmlns:d=&quot;http://schemas.microsoft.com/expression/blend/2008&quot;
mc:Ignorable=&quot;d&quot;
d:DesignHeight=&quot;30&quot; d:DesignWidth=&quot;200&quot;&gt;
&lt;Grid&gt;
&lt;Grid.ColumnDefinitions&gt;
&lt;ColumnDefinition Width=&quot;*&quot;/&gt;
&lt;ColumnDefinition Width=&quot;*&quot;/&gt;
&lt;ColumnDefinition Width=&quot;*&quot;/&gt;
&lt;ColumnDefinition Width=&quot;Auto&quot;/&gt;
&lt;ColumnDefinition Width=&quot;Auto&quot;/&gt;
&lt;/Grid.ColumnDefinitions&gt;
&lt;!-- Standard Mode Panel --&gt;
&lt;StackPanel x:Name=&quot;StandardModePanel&quot; Orientation=&quot;Horizontal&quot; Grid.ColumnSpan=&quot;5&quot;&gt;
&lt;TextBox x:Name=&quot;XTextBox&quot; Width=&quot;60&quot; Margin=&quot;0,0,2,0&quot; TextChanged=&quot;XTextBox_TextChanged&quot; IsReadOnly=&quot;{Binding IsReadOnly}&quot;/&gt;
&lt;TextBox x:Name=&quot;YTextBox&quot; Width=&quot;60&quot; Margin=&quot;0,0,2,0&quot; TextChanged=&quot;YTextBox_TextChanged&quot; IsReadOnly=&quot;{Binding IsReadOnly}&quot;/&gt;
&lt;TextBox x:Name=&quot;ZTextBox&quot; Width=&quot;60&quot; Margin=&quot;0,0,2,0&quot; TextChanged=&quot;ZTextBox_TextChanged&quot; IsReadOnly=&quot;{Binding IsReadOnly}&quot;/&gt;
&lt;Button Content=&quot;{DynamicResource Vec3d_TextMode_Toggle}&quot; Width=&quot;20&quot; Click=&quot;TextModeToggle_Click&quot; Margin=&quot;0,0,2,0&quot; ToolTip=&quot;{DynamicResource Vec3d_TextMode_Tooltip}&quot;/&gt;
&lt;Button Content=&quot;{DynamicResource Vec3d_Normalize}&quot; Width=&quot;20&quot; Click=&quot;NormalizeButton_Click&quot; Visibility=&quot;{Binding ShowNormalizeButton, Converter={StaticResource BooleanToVisibilityConverter}}&quot; ToolTip=&quot;{DynamicResource Vec3d_Normalize_Tooltip}&quot;/&gt;
&lt;/StackPanel&gt;
&lt;!-- Text Mode Panel --&gt;
&lt;StackPanel x:Name=&quot;TextModePanel&quot; Orientation=&quot;Horizontal&quot; Grid.ColumnSpan=&quot;5&quot; Visibility=&quot;Collapsed&quot;&gt;
&lt;TextBox x:Name=&quot;VectorTextBox&quot; Width=&quot;180&quot; Margin=&quot;0,0,2,0&quot; TextChanged=&quot;VectorTextBox_TextChanged&quot; IsReadOnly=&quot;{Binding IsReadOnly}&quot;
ToolTip=&quot;{DynamicResource Vec3d_TextMode_Format_Tooltip}&quot;/&gt;
&lt;Button Content=&quot;{DynamicResource Vec3d_StandardMode_Toggle}&quot; Width=&quot;20&quot; Click=&quot;StandardModeToggle_Click&quot; Margin=&quot;0,0,2,0&quot; ToolTip=&quot;{DynamicResource Vec3d_StandardMode_Tooltip}&quot;/&gt;
&lt;Button Content=&quot;{DynamicResource Vec3d_Normalize}&quot; Width=&quot;20&quot; Click=&quot;NormalizeButton_Click&quot; Visibility=&quot;{Binding ShowNormalizeButton, Converter={StaticResource BooleanToVisibilityConverter}}&quot; ToolTip=&quot;{DynamicResource Vec3d_Normalize_Tooltip}&quot;/&gt;
&lt;/StackPanel&gt;
&lt;/Grid&gt;
&lt;/UserControl&gt;
</code></pre><pre><code class="lang-csharp" name="SampleCode-xaml.cs">using System;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows;
using Hi.Geom;
using System.Text.RegularExpressions;
using System.ComponentModel;
using Hi.Common;
using Hi.Common.Messages;
namespace HiNC_2025_win_desktop.Geom
{
/// &lt;summary&gt;
/// Vec3dControl.xaml 的交互逻辑
/// &lt;/summary&gt;
public partial class Vec3dControl : UserControl, INotifyPropertyChanged
{
private bool _isUpdating = false;
private Func&lt;Vec3d&gt; _getterFunc;
private Func&lt;Task&gt; _updateByContentFunc;
private bool _showNormalizeButton = false;
private bool _isTextMode = false;
private bool _isReadOnly = false;
private static readonly Regex VectorRegex = new Regex(@&quot;^\s*[\(\[\{]?\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*[\)\]\}]?\s*$&quot;, RegexOptions.Compiled);
private static readonly Regex MatrixRegex = new Regex(@&quot;\{(?:\s*\{?\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*\}?\s*,){3}\s*\{?\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*,\s*(-?\d*\.?\d+)\s*\}?\s*\}&quot;, RegexOptions.Compiled);
public event PropertyChangedEventHandler PropertyChanged;
public string ControlId { get; set; } = Guid.NewGuid().ToString();
public Func&lt;Vec3d&gt; GetterFunc
{
get =&gt; _getterFunc;
set
{
_getterFunc = value;
UpdateUI();
}
}
public Func&lt;Task&gt; UpdateByContentFunc
{
get =&gt; _updateByContentFunc;
set =&gt; _updateByContentFunc = value;
}
public bool ShowNormalizeButton
{
get =&gt; _showNormalizeButton;
set
{
if (_showNormalizeButton != value)
{
_showNormalizeButton = value;
OnPropertyChanged(nameof(ShowNormalizeButton));
}
}
}
public bool IsTextMode
{
get =&gt; _isTextMode;
set
{
if (_isTextMode != value)
{
_isTextMode = value;
UpdateModeVisibility();
OnPropertyChanged(nameof(IsTextMode));
}
}
}
public bool IsReadOnly
{
get =&gt; _isReadOnly;
set
{
if (_isReadOnly != value)
{
_isReadOnly = value;
OnPropertyChanged(nameof(IsReadOnly));
}
}
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public Vec3dControl()
{
InitializeComponent();
DataContext = this;
}
private void UpdateModeVisibility()
{
if (IsTextMode)
{
StandardModePanel.Visibility = Visibility.Collapsed;
TextModePanel.Visibility = Visibility.Visible;
UpdateVectorTextFromXYZ();
}
else
{
StandardModePanel.Visibility = Visibility.Visible;
TextModePanel.Visibility = Visibility.Collapsed;
}
}
private void UpdateVectorTextFromXYZ()
{
if (_isUpdating) return;
if (double.TryParse(XTextBox.Text, out double x) &amp;&amp;
double.TryParse(YTextBox.Text, out double y) &amp;&amp;
double.TryParse(ZTextBox.Text, out double z))
{
VectorTextBox.Text = $&quot;{x},{y},{z}&quot;;
}
}
private void TextModeToggle_Click(object sender, RoutedEventArgs e)
{
IsTextMode = true;
}
private void StandardModeToggle_Click(object sender, RoutedEventArgs e)
{
IsTextMode = false;
}
private async void NormalizeButton_Click(object sender, RoutedEventArgs e)
{
if (_isUpdating || _getterFunc == null || IsReadOnly)
return;
try
{
_isUpdating = true;
var vec = _getterFunc();
if (vec != null)
{
vec.Normalize();
XTextBox.Text = vec.X.ToString(&quot;F3&quot;);
YTextBox.Text = vec.Y.ToString(&quot;F3&quot;);
ZTextBox.Text = vec.Z.ToString(&quot;F3&quot;);
if (IsTextMode)
{
VectorTextBox.Text = $&quot;{vec.X:F3},{vec.Y:F3},{vec.Z:F3}&quot;;
}
if(_updateByContentFunc != null)
await _updateByContentFunc();
}
}
catch (Exception ex)
{
MessageKit.AddError(string.Format(Application.Current.FindResource(&quot;Vec3d_Update_Error&quot;).ToString(), ex.Message));
ex.ShowException(this);
}
finally
{
_isUpdating = false;
}
}
public void UpdateUI()
{
if (_isUpdating || _getterFunc == null)
return;
try
{
_isUpdating = true;
var vec = _getterFunc();
if (vec != null)
{
XTextBox.Text = vec.X.ToString(&quot;F3&quot;);
YTextBox.Text = vec.Y.ToString(&quot;F3&quot;);
ZTextBox.Text = vec.Z.ToString(&quot;F3&quot;);
if (IsTextMode)
{
VectorTextBox.Text = $&quot;{vec.X},{vec.Y},{vec.Z}&quot;;
}
}
else
{
XTextBox.Text = &quot;&quot;;
YTextBox.Text = &quot;&quot;;
ZTextBox.Text = &quot;&quot;;
VectorTextBox.Text = &quot;&quot;;
}
}
finally
{
_isUpdating = false;
}
}
private async void XTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
await HandleTextChanged();
}
private async void YTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
await HandleTextChanged();
}
private async void ZTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
await HandleTextChanged();
}
private async void VectorTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (_isUpdating || _getterFunc == null || IsReadOnly)
return;
try
{
string text = VectorTextBox.Text.Trim();
// 如果文本为空或太短,不做处理
if (string.IsNullOrWhiteSpace(text) || text.Length &lt; 3)
return;
_isUpdating = true;
// 尝试解析为向量格式
if (TryParseVector(text, out double x, out double y, out double z))
{
await UpdateVectorValues(x, y, z);
}
// 尝试解析为变换矩阵格式(仅提取位移分量)
else if (TryParseTransformMatrix(text, out double tx, out double ty, out double tz))
{
await UpdateVectorValues(tx, ty, tz);
}
// 尝试作为单值解析每个字段(宽松模式)
else if (TryParseLooseVector(text, out double lx, out double ly, out double lz))
{
await UpdateVectorValues(lx, ly, lz);
}
}
catch (Exception ex)
{
MessageKit.AddError(string.Format(Application.Current.FindResource(&quot;Vec3d_Update_Error&quot;).ToString(), ex.Message));
ex.ShowException(this);
}
finally
{
_isUpdating = false;
}
}
private bool TryParseVector(string text, out double x, out double y, out double z)
{
x = y = z = 0;
// 使用正则表达式匹配向量格式
Match match = VectorRegex.Match(text);
if (match.Success &amp;&amp; match.Groups.Count &gt;= 4)
{
if (double.TryParse(match.Groups[1].Value, out x) &amp;&amp;
double.TryParse(match.Groups[2].Value, out y) &amp;&amp;
double.TryParse(match.Groups[3].Value, out z))
{
return true;
}
}
return false;
}
private bool TryParseTransformMatrix(string text, out double tx, out double ty, out double tz)
{
tx = ty = tz = 0;
// 移除所有换行和多余空格,便于匹配
text = Regex.Replace(text, @&quot;\s+&quot;, &quot; &quot;);
// 尝试匹配变换矩阵格式
Match match = MatrixRegex.Match(text);
if (match.Success &amp;&amp; match.Groups.Count &gt;= 8)
{
// 变换矩阵的第4列通常是位移分量
if (double.TryParse(match.Groups[4].Value, out tx) &amp;&amp;
double.TryParse(match.Groups[8].Value, out ty) &amp;&amp;
double.TryParse(match.Groups[12].Value, out tz))
{
return true;
}
}
// 尝试匹配更宽松的变换矩阵表示(例如从界面复制的数据)
var numbers = Regex.Matches(text, @&quot;(-?\d*\.?\d+)&quot;);
if (numbers.Count &gt;= 16)
{
// 假设这是一个4x4矩阵提取位移分量第4、8、12个数字
if (double.TryParse(numbers[3].Value, out tx) &amp;&amp;
double.TryParse(numbers[7].Value, out ty) &amp;&amp;
double.TryParse(numbers[11].Value, out tz))
{
return true;
}
}
return false;
}
private bool TryParseLooseVector(string text, out double x, out double y, out double z)
{
x = y = z = 0;
// 移除所有非数字和小数点以外的字符,然后按空白分割
string cleanText = Regex.Replace(text, @&quot;[^\d\.\-\s,;]+&quot;, &quot; &quot;);
string[] parts = Regex.Split(cleanText, @&quot;[\s,;]+&quot;);
// 尝试从分割后的部分获取三个数字
var numbers = new System.Collections.Generic.List&lt;double&gt;();
foreach (var part in parts)
{
if (!string.IsNullOrWhiteSpace(part) &amp;&amp; double.TryParse(part, out double value))
{
numbers.Add(value);
if (numbers.Count &gt;= 3) break; // 最多取3个数字
}
}
// 如果获取到了三个数字,就认为解析成功
if (numbers.Count &gt;= 3)
{
x = numbers[0];
y = numbers[1];
z = numbers[2];
return true;
}
return false;
}
private async Task UpdateVectorValues(double x, double y, double z)
{
XTextBox.Text = x.ToString(&quot;F3&quot;);
YTextBox.Text = y.ToString(&quot;F3&quot;);
ZTextBox.Text = z.ToString(&quot;F3&quot;);
var vec = _getterFunc?.Invoke();
if (vec != null)
{
vec.X = x;
vec.Y = y;
vec.Z = z;
if (_updateByContentFunc != null)
{
await _updateByContentFunc();
}
}
}
private async Task HandleTextChanged()
{
if (_isUpdating || _getterFunc == null || IsReadOnly)
return;
try
{
_isUpdating = true;
// 尝试解析每个文本框的值
bool allValid = true;
allValid &amp;= double.TryParse(XTextBox.Text, out double x);
allValid &amp;= double.TryParse(YTextBox.Text, out double y);
allValid &amp;= double.TryParse(ZTextBox.Text, out double z);
if (allValid)
{
if (IsTextMode)
{
VectorTextBox.Text = $&quot;{x},{y},{z}&quot;;
}
var vec = _getterFunc();
if (vec != null)
{
vec.X = x;
vec.Y = y;
vec.Z = z;
if(_updateByContentFunc != null)
await _updateByContentFunc();
}
}
else
{
// 如果有无效输入,不进行更新但也不显示错误
// 这允许用户在输入过程中有不完整的状态
}
}
catch (Exception ex)
{
// 记录异常但不中断用户操作
MessageKit.AddError(string.Format(Application.Current.FindResource(&quot;Vec3d_Update_Error&quot;).ToString(), ex.Message));
ex.ShowException(this);
}
finally
{
_isUpdating = false;
}
}
}
}
</code></pre><h2 id="source-code-path">Source Code Path</h2>
<p>See <a href="../../index.html">this page</a> for git repository.</p>
<h3 id="wpf-application-source-code-path">WPF Application Source Code Path</h3>
<ul>
<li>Geom/Vec3dControl</li>
</ul>
<h3 id="web-service-application-source-code-path">Web Service Application Source Code Path</h3>
<ul>
<li>wwwroot/widget/vec3d-control.js</li>
<li>Widget/Vec3dHub.cs</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span> Copyright © 2025 <a href='https://superhightech.com.tw'>Tech Coordinate</a>. All rights reserved. <a href='https://superhightech.com.tw'>超級高科技股份有限公司</a> © 2025 版權所有 </span>
</div>
</div>
</footer>
</body>
</html>