feat: integrate FaceInferToolPlugin with Rail assembly end face detection

- Add '快速取端面' button in Rail assembly workflow
- Single click → FaceInferToolPlugin → AssemblyEndFaceAnalyzer.Analyze(hitPoint, normal)
- Replaces 3-click manual seed point workflow
This commit is contained in:
tian 2026-06-09 01:53:36 +08:00
parent e7b5206198
commit 16bf5cc10e
2 changed files with 109 additions and 0 deletions

View File

@ -12,6 +12,7 @@ using System.Text;
using System.Numerics;
using Microsoft.Win32;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Plugins;
using NavisApplication = Autodesk.Navisworks.Api.Application;
using NavisworksTransport.Core;
using NavisworksTransport.Core.Config;
@ -1411,6 +1412,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public ICommand CancelRailAssemblyWorkflowCommand { get; private set; }
public ICommand AnalyzeAssemblyTerminalFaceForCreateCommand { get; private set; }
public ICommand AnalyzeAssemblyTerminalFaceForEditCommand { get; private set; }
public ICommand QuickAnalyzeAssemblyEndFaceCommand { get; private set; }
public ICommand DecreaseSelectedRailNormalOffsetCommand { get; private set; }
public ICommand IncreaseSelectedRailNormalOffsetCommand { get; private set; }
@ -1482,6 +1484,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
!IsSelectingAssemblyStartPoint &&
!IsSelectingAssemblyEndFacePoints &&
!_isSelectingAssemblyInstallationPoint;
public bool CanQuickAnalyzeAssemblyEndFace => CanAnalyzeAssemblyTerminalFaceForCreate;
public bool CanAnalyzeAssemblyTerminalFaceForEdit => HasAssemblyTerminalObject &&
IsRailRouteSelected &&
SelectedPathRoute != null &&
@ -1691,6 +1694,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private void NotifyRailAssemblyCommandStateChanged()
{
OnPropertyChanged(nameof(CanAnalyzeAssemblyTerminalFaceForCreate));
OnPropertyChanged(nameof(CanQuickAnalyzeAssemblyEndFace));
OnPropertyChanged(nameof(CanAnalyzeAssemblyTerminalFaceForEdit));
OnPropertyChanged(nameof(CanSelectAssemblyStartPointForCreate));
OnPropertyChanged(nameof(CanRepositionRailStartPoint));
@ -2073,6 +2077,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
CancelRailAssemblyWorkflowCommand = new RelayCommand(() => ExecuteCancelRailAssemblyWorkflow(), () => CanCancelRailAssemblyWorkflow);
AnalyzeAssemblyTerminalFaceForCreateCommand = new RelayCommand(async () => await ExecuteAnalyzeAssemblyTerminalFaceAsync(RailAssemblyWorkflowMode.CreateNewRailPath), () => CanAnalyzeAssemblyTerminalFaceForCreate);
AnalyzeAssemblyTerminalFaceForEditCommand = new RelayCommand(async () => await ExecuteAnalyzeAssemblyTerminalFaceAsync(RailAssemblyWorkflowMode.EditSelectedRail), () => CanAnalyzeAssemblyTerminalFaceForEdit);
QuickAnalyzeAssemblyEndFaceCommand = new RelayCommand(() => BeginAssemblyEndFaceQuickPick(), () => CanQuickAnalyzeAssemblyEndFace);
DecreaseSelectedRailNormalOffsetCommand = new RelayCommand(() => AdjustSelectedRailNormalOffset(-RailNormalOffsetNudgeStepInMeters));
IncreaseSelectedRailNormalOffsetCommand = new RelayCommand(() => AdjustSelectedRailNormalOffset(RailNormalOffsetNudgeStepInMeters));
ApplyHoistingLayerHeightsCommand = new RelayCommand(async () => await ExecuteApplyHoistingLayerHeightsAsync(), () => CanApplyHoistingLayerHeights);
@ -2477,6 +2482,105 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Info(logMessage);
}
private void BeginAssemblyEndFaceQuickPick()
{
CleanupAssemblyReferenceSelection();
CleanupAssemblyEndFaceSelection(clearVisuals: false);
CleanupAssemblyInstallationSelection();
ClearAssemblyEndFaceAnalysisVisuals();
ResetAssemblyEndFaceAnalysisState();
ClearAssemblyInstallationReferenceVisuals();
ResetAssemblyInstallationReferenceState();
_assemblyEndFaceSeedPoints.Clear();
_pathPlanningManager.DisableMouseHandling();
_isSelectingAssemblyEndFacePoints = true;
NotifyRailAssemblyCommandStateChanged();
// 激活取面工具
try
{
var assemblyPath = FaceInferToolPlugin.AssemblyPath;
NavisApplication.Plugins.AddPluginAssembly(assemblyPath);
var record = (ToolPluginRecord)NavisApplication.Plugins.FindPlugin("FaceInferTool.NavisworksTransport");
var loadedPlugin = record.LoadPlugin();
NavisApplication.MainDocument.Tool.Value = Tool.None;
NavisApplication.MainDocument.Tool.SetCustomToolPlugin(loadedPlugin);
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 激活取面工具失败: {ex.Message}", ex);
CleanupAssemblyEndFaceSelection(clearVisuals: true);
throw new InvalidOperationException("取面工具激活失败,请重试。");
}
FaceInferToolPlugin.FaceInferred += OnQuickEndFaceInferred;
UpdateMainStatus("请在终点箱体端面上点击一下,系统将自动分析端面中心。");
LogManager.Info("[直线装配] 已进入快速取端面模式");
}
private void OnQuickEndFaceInferred(object sender, FaceInferResult result)
{
FaceInferToolPlugin.FaceInferred -= OnQuickEndFaceInferred;
// 恢复选择工具
try { NavisApplication.MainDocument.Tool.Value = Tool.Select; } catch { }
if (!result.IsValid || result.ModelItem != _assemblyTerminalObject)
{
CleanupAssemblyEndFaceSelection(clearVisuals: false);
UpdateMainStatus("取面失败: 请确认点击的是终点箱体的端面。");
return;
}
try
{
var endFaceResult = AssemblyEndFaceAnalyzer.Analyze(
_assemblyTerminalObject,
result.HitPoint,
result.Normal);
if (!endFaceResult.IsReliable)
{
CleanupAssemblyEndFaceSelection(clearVisuals: false);
UpdateMainStatus($"端面分析失败: {endFaceResult.DiagnosticMessage}");
return;
}
Point3D centerPoint = AssemblyEndFaceAnalyzer.ToPoint3D(endFaceResult.Center);
Point3D sphereCenterPoint = new Point3D(
AssemblySphereCenterX, AssemblySphereCenterY, AssemblySphereCenterZ);
Vector3 orientedNormal = AssemblyEndFaceAnalyzer.OrientNormalTowardTarget(
endFaceResult.Normal,
new Vector3((float)centerPoint.X, (float)centerPoint.Y, (float)centerPoint.Z),
new Vector3((float)sphereCenterPoint.X, (float)sphereCenterPoint.Y, (float)sphereCenterPoint.Z));
_hasAssemblyEndFaceAnalysis = true;
_assemblyEndFaceCenterPoint = centerPoint;
_assemblyEndFaceNormal = orientedNormal;
RenderAssemblyEndFaceCenter(centerPoint);
RenderAssemblyEndFaceNormal(centerPoint, orientedNormal);
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
UpdateMainStatus($"端面分析完成:中心=({centerPoint.X:F2}, {centerPoint.Y:F2}, {centerPoint.Z:F2}),三角形={endFaceResult.CandidateTriangleCount}");
LogManager.Info(
$"[直线装配] 快速取端面完成: 中心=({centerPoint.X:F3}, {centerPoint.Y:F3}, {centerPoint.Z:F3}), " +
$"法向=({orientedNormal.X:F4}, {orientedNormal.Y:F4}, {orientedNormal.Z:F4}), " +
$"三角形={endFaceResult.CandidateTriangleCount}");
}
catch (Exception ex)
{
LogManager.Error($"[直线装配] 端面分析失败: {ex.Message}", ex);
UpdateMainStatus($"端面分析失败: {ex.Message}");
}
finally
{
_isSelectingAssemblyEndFacePoints = false;
NotifyRailAssemblyCommandStateChanged();
}
}
private void BeginAssemblyEndFaceSelection(RailAssemblyWorkflowMode workflowMode)
{
CleanupAssemblyReferenceSelection();

View File

@ -538,6 +538,11 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
Command="{Binding AnalyzeAssemblyTerminalFaceForCreateCommand}"
Style="{StaticResource SecondaryButtonStyle}"
IsEnabled="{Binding CanAnalyzeAssemblyTerminalFaceForCreate}"/>
<Button Content="快速取端面"
Command="{Binding QuickAnalyzeAssemblyEndFaceCommand}"
Style="{StaticResource SecondaryButtonStyle}"
IsEnabled="{Binding CanQuickAnalyzeAssemblyEndFace}"
ToolTip="点击一次即可自动分析端面(使用取面工具)"/>
<Button Content="{Binding CreateAssemblyInstallationPointButtonText}"
Command="{Binding SelectAssemblyInstallationPointForCreateCommand}"
Style="{StaticResource SecondaryButtonStyle}"