From 1d71d36e5fb1195ada95add7d2777d787ad5eda4 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Mon, 30 Mar 2026 22:13:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drail=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E7=9A=84=E7=BC=96=E8=BE=91=E7=8A=B6=E6=80=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 44 ++++++ src/UI/WPF/ViewModels/PathEditingViewModel.cs | 138 ++++++++++++++++-- src/UI/WPF/Views/PathEditingView.xaml | 4 +- src/Utils/ModelItemTransformHelper.cs | 4 +- 4 files changed, 173 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b58d67a..0bc6113 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -133,6 +133,50 @@ 禁止再使用“本地坐标系”这种含糊说法。 +### 4.1.2 坐标命名规则 + +以后凡是变量名、字段名、日志名里出现 `X/Y/Z`、正负轴、forward/up/side 等方向语义,必须在名字上直接带出所属坐标系,避免只看名字时无法判断语义层。 + +允许的前缀示例: + +- `Host...` + - 宿主坐标系 +- `Canonical...` + - 内部坐标系 +- `Asset...` + - 资产坐标系 +- `Local...` + - 仅当这里明确表示“对象自身局部轴”时才允许使用 + +正负轴命名示例: + +- `LocalPositiveX` +- `LocalNegativeY` +- `HostPositiveZ` +- `CanonicalPositiveX` +- `AssetPositiveY` + +方向/向量命名示例: + +- `hostForward` +- `hostUp` +- `canonicalForward` +- `assetUp` +- `localXAxis` +- `localZAxis` + +禁止继续使用这类脱离坐标系语义的名字: + +- `PositiveX` +- `NegativeZ` +- `xAxis` +- `upAxis` +- `forwardAxis` + +除非变量名里已经明确出现了所属坐标系前缀。 + +目标是做到:只看名字,就能知道这个方向、轴、向量到底属于宿主、内部、资产,还是对象自身局部轴。 + ### 4.1.1 Quaternion / Rotation3D 固定定义 这是项目级硬约束,不允许在任何修复中重新猜测、重新验证或临时改口: diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs index 35213ad..a650edd 100644 --- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs +++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs @@ -11,7 +11,6 @@ using System.IO; 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; @@ -21,7 +20,6 @@ using NavisworksTransport.UI.WPF.Models; using NavisworksTransport.Utils; using NavisworksTransport.Utils.CoordinateSystem; using NavisworksTransport.Utils.GeometryAnalysis; -using NavisworksTransport; namespace NavisworksTransport.UI.WPF.ViewModels { @@ -145,6 +143,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels private bool _isEditingSelectedRailStartPoint; private bool _isSelectingAssemblyEndFacePoints; private bool _isSelectingAssemblyInstallationPoint; + private bool _isEditingSelectedRailInstallationPoint; + private bool _isEditingSelectedRail; private bool _hasAssemblyEndFaceAnalysis; private bool _hasAssemblyInstallationReference; private ModelItem _assemblyTerminalObject; @@ -218,6 +218,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels ClearAssemblyReferenceLineVisuals(); ClearAssemblyInstallationReferenceVisuals(); + // 重置 Rail 编辑状态 + _isEditingSelectedRail = false; + OnPropertyChanged(nameof(AssemblyInstallationPointButtonText)); + // 🔧 修复:同步PathPlanningManager的CurrentRoute if (_pathPlanningManager != null && value != null) { @@ -1066,6 +1070,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels public ICommand ExportPathCommand { get; private set; } public ICommand SaveAsPathCommand { get; private set; } public ICommand CaptureAssemblyTerminalObjectCommand { get; private set; } + public ICommand ReCaptureAssemblyTerminalObjectCommand { get; private set; } public ICommand GenerateAssemblyReferenceRodCommand { get; private set; } public ICommand SelectAssemblyStartPointCommand { get; private set; } public ICommand RepositionRailStartPointCommand { get; private set; } @@ -1291,6 +1296,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels private bool HasSelectedRailInstallationReference() { + // 只有在点击"重选箱体"进入编辑状态后才显示"重选安装点" + if (!_isEditingSelectedRail) + { + return false; + } + var coreRoute = GetSelectedCoreRoute(); return coreRoute != null && coreRoute.PathType == PathType.Rail && @@ -1376,11 +1387,30 @@ namespace NavisworksTransport.UI.WPF.ViewModels return; } - var coreRoute = GetSelectedCoreRoute(); - if (!TryBuildRailRouteReferenceLine(coreRoute, out AssemblyReferenceLine referenceLine)) + AssemblyReferenceLine referenceLine; + + if (_isEditingSelectedRail) { - ClearAssemblyReferenceLineVisuals(); - return; + // 编辑状态:使用默认杆长度构建参考线,方便选择更远的起点 + try + { + referenceLine = BuildAssemblyReferenceLine(); + } + catch + { + ClearAssemblyReferenceLineVisuals(); + return; + } + } + else + { + // 非编辑状态:使用路径实际长度 + var coreRoute = GetSelectedCoreRoute(); + if (!TryBuildRailRouteReferenceLine(coreRoute, out referenceLine)) + { + ClearAssemblyReferenceLineVisuals(); + return; + } } AssemblyReferencePathManager.Instance.CreateOrUpdateReferenceRod( @@ -1422,11 +1452,31 @@ namespace NavisworksTransport.UI.WPF.ViewModels throw new InvalidOperationException("当前 Rail 路径没有可更新的终点。"); } + // 获取当前起点索引 + int startPointIndex = GetRailRouteStartPointIndex(route); + if (startPointIndex < 0) + { + throw new InvalidOperationException("当前 Rail 路径没有可更新的起点。"); + } + + // 计算原路径长度(起点到终点的距离) + Point3D currentStartPoint = route.Points[startPointIndex].Position; + Point3D currentEndPoint = route.Points[installationPointIndex].Position; + double pathLength = GeometryHelper.CalculatePointDistance(currentStartPoint, currentEndPoint); + + // 更新终点(安装点) if (!_pathPlanningManager.UpdatePathPointWithConstraints(route, installationPointIndex, _assemblyInstallationAnchorPoint)) { throw new InvalidOperationException("更新 Rail 路径安装点失败。"); } + // 根据新终点和原路径长度重新计算起点位置 + Point3D newStartPoint = CalculateRailStartPointFromEndPoint(_assemblyInstallationAnchorPoint, pathLength); + if (!_pathPlanningManager.UpdatePathPointWithConstraints(route, startPointIndex, newStartPoint)) + { + throw new InvalidOperationException("更新 Rail 路径起点失败。"); + } + route.RailPreferredNormal = new Point3D( _assemblyInstallationPlaneNormal.X, _assemblyInstallationPlaneNormal.Y, @@ -1449,7 +1499,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels RefreshSelectedRailReferenceLineVisuals(); } - LogManager.Info($"[Rail构型] {route.Name}: {logMessage}"); + LogManager.Info($"[Rail构型] {route.Name}: {logMessage},起点已同步更新为 ({newStartPoint.X:F3}, {newStartPoint.Y:F3}, {newStartPoint.Z:F3})"); } private void RefreshSelectedRailInstallationReferenceVisuals() @@ -1622,6 +1672,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels ExportPathCommand = new RelayCommand(async () => await ExecuteExportPathAsync(), () => CanExecuteExportPath); SaveAsPathCommand = new RelayCommand(async () => await ExecuteSaveAsPathAsync(), () => CanExecuteSaveAsPath); CaptureAssemblyTerminalObjectCommand = new RelayCommand(async () => await ExecuteCaptureAssemblyTerminalObjectAsync()); + ReCaptureAssemblyTerminalObjectCommand = new RelayCommand(async () => await ExecuteReCaptureAssemblyTerminalObjectAsync()); GenerateAssemblyReferenceRodCommand = new RelayCommand(async () => await ExecuteGenerateAssemblyReferenceRodAsync(), () => CanGenerateAssemblyReferenceRod); SelectAssemblyStartPointCommand = new RelayCommand(async () => await ExecuteSelectAssemblyStartPointAsync(), () => CanSelectAssemblyStartPoint); RepositionRailStartPointCommand = new RelayCommand(async () => await ExecuteRepositionRailStartPointAsync(), () => CanRepositionRailStartPoint); @@ -1656,6 +1707,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels throw new InvalidOperationException("当前选择对象无效,请重新选择终点箱体"); } + // 注意:不在这里设置 _isEditingSelectedRail + // 新建路径时保持 false,只有点击"重选箱体"时才设置为 true + _assemblyTerminalObject = selectedItem; _assemblyStartPoint = Point3D.Origin; HasAssemblyTerminalObject = true; @@ -1672,12 +1726,24 @@ namespace NavisworksTransport.UI.WPF.ViewModels OnPropertyChanged(nameof(CanRepositionRailStartPoint)); OnPropertyChanged(nameof(CanSelectAssemblyInstallationPoint)); OnPropertyChanged(nameof(CanAnalyzeAssemblyTerminalFace)); - RefreshSelectedRailReferenceLineVisuals($"已捕获终点箱体: {AssemblyTerminalObjectName}"); + OnPropertyChanged(nameof(AssemblyInstallationPointButtonText)); + + // 新建路径时隐藏其他路径的辅助线 + ClearAssemblyReferenceLineVisuals(); + UpdateMainStatus($"已捕获终点箱体: {AssemblyTerminalObjectName}"); LogManager.Info($"[直线装配] 已捕获终点箱体: {AssemblyTerminalObjectName}"); }, "捕获终点箱体"); } + private async Task ExecuteReCaptureAssemblyTerminalObjectAsync() + { + // 先进入编辑状态,再执行捕获 + _isEditingSelectedRail = true; + OnPropertyChanged(nameof(AssemblyInstallationPointButtonText)); + await ExecuteCaptureAssemblyTerminalObjectAsync(); + } + private void ClearNonGridPathVisualizations(string context) { if (PathPointRenderPlugin.Instance == null) @@ -1915,6 +1981,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels _pathPlanningManager.DisableMouseHandling(); _isSelectingAssemblyInstallationPoint = true; + + // 判断是编辑现有路径还是创建新路径 + var selectedRailRoute = GetSelectedCoreRoute(); + _isEditingSelectedRailInstallationPoint = selectedRailRoute != null && selectedRailRoute.PathType == PathType.Rail; + OnPropertyChanged(nameof(CanAnalyzeAssemblyTerminalFace)); OnPropertyChanged(nameof(CanSelectAssemblyStartPoint)); OnPropertyChanged(nameof(CanRepositionRailStartPoint)); @@ -2048,12 +2119,22 @@ namespace NavisworksTransport.UI.WPF.ViewModels BuildAndRenderAssemblyInstallationReference( _assemblyInstallationSeedPoints[0], _assemblyInstallationSeedPoints[1]); - var selectedRailRoute = GetSelectedCoreRoute(); - if (selectedRailRoute != null && selectedRailRoute.PathType == PathType.Rail) + + if (_isEditingSelectedRailInstallationPoint) { - PersistAssemblyInstallationReferenceToRailRoute( - selectedRailRoute, - "已重选安装点并同步更新 Rail 路径安装参数"); + // 编辑现有路径:更新安装点并同步起点 + var selectedRailRoute = GetSelectedCoreRoute(); + if (selectedRailRoute != null && selectedRailRoute.PathType == PathType.Rail) + { + PersistAssemblyInstallationReferenceToRailRoute( + selectedRailRoute, + "已重选安装点并同步更新 Rail 路径安装参数"); + } + } + else + { + // 创建新路径:生成参考线,等待用户选择起点 + LogManager.Info("[直线装配] 安装点已确定,请继续选择起点以生成新路径"); } CleanupAssemblyInstallationSelection(); }, "处理安装点拾取"); @@ -2224,6 +2305,36 @@ namespace NavisworksTransport.UI.WPF.ViewModels return adapter.FromCanonicalPoint(canonicalAnchorPoint); } + /// + /// 根据 Rail 路径终点和路径长度计算起点位置。 + /// 起点位于从终点沿光轴方向(远离球心方向)延伸路径长度的位置。 + /// + private Point3D CalculateRailStartPointFromEndPoint(Point3D endPoint, double pathLength) + { + HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter(); + ProjectReferenceFrame projectFrame = CreateAssemblyProjectReferenceFrame(adapter); + + Point3D opticalAxisReferencePoint = GetAssemblyOpticalAxisReferencePoint(); + Point3D canonicalEndPoint = adapter.ToCanonicalPoint(endPoint); + Point3D canonicalOpticalAxisRef = adapter.ToCanonicalPoint(opticalAxisReferencePoint); + + // 计算从球心到光轴参考点的方向(作为参考线方向) + Vector3D direction = new Vector3D( + canonicalOpticalAxisRef.X - projectFrame.SphereCenterInCanonical.X, + canonicalOpticalAxisRef.Y - projectFrame.SphereCenterInCanonical.Y, + canonicalOpticalAxisRef.Z - projectFrame.SphereCenterInCanonical.Z); + + direction = direction.Normalize(); + + // 起点 = 终点 + 方向 * 路径长度 + Point3D canonicalStartPoint = new Point3D( + canonicalEndPoint.X + direction.X * pathLength, + canonicalEndPoint.Y + direction.Y * pathLength, + canonicalEndPoint.Z + direction.Z * pathLength); + + return adapter.FromCanonicalPoint(canonicalStartPoint); + } + private void UpdateAssemblyInstallationAnchorFromVerticalOffset() { if (!_hasAssemblyInstallationReference || @@ -2997,6 +3108,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { PathClickToolPlugin.MouseClicked -= OnAssemblyInstallationMouseClicked; _isSelectingAssemblyInstallationPoint = false; + _isEditingSelectedRailInstallationPoint = false; _pathPlanningManager?.EnableMouseHandling(); _pathPlanningManager?.StopClickTool(); ClearAssemblyInstallationReferenceVisuals(); diff --git a/src/UI/WPF/Views/PathEditingView.xaml b/src/UI/WPF/Views/PathEditingView.xaml index 0249bdd..b17ac0d 100644 --- a/src/UI/WPF/Views/PathEditingView.xaml +++ b/src/UI/WPF/Views/PathEditingView.xaml @@ -560,10 +560,10 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管 Orientation="Horizontal" VerticalAlignment="Center" ItemHeight="28"> -