From 16bf5cc10edafe0e1b3abdefe0977a339aa2c4f6 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Tue, 9 Jun 2026 01:53:36 +0800 Subject: [PATCH] feat: integrate FaceInferToolPlugin with Rail assembly end face detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add '快速取端面' button in Rail assembly workflow - Single click → FaceInferToolPlugin → AssemblyEndFaceAnalyzer.Analyze(hitPoint, normal) - Replaces 3-click manual seed point workflow --- src/UI/WPF/ViewModels/PathEditingViewModel.cs | 104 ++++++++++++++++++ src/UI/WPF/Views/PathEditingView.xaml | 5 + 2 files changed, 109 insertions(+) diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index 528bbf2..ca64002 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -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(); diff --git a/src/UI/WPF/Views/PathEditingView.xaml b/src/UI/WPF/Views/PathEditingView.xaml index 9960293..8885761 100644 --- a/src/UI/WPF/Views/PathEditingView.xaml +++ b/src/UI/WPF/Views/PathEditingView.xaml @@ -538,6 +538,11 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管 Command="{Binding AnalyzeAssemblyTerminalFaceForCreateCommand}" Style="{StaticResource SecondaryButtonStyle}" IsEnabled="{Binding CanAnalyzeAssemblyTerminalFaceForCreate}"/> +