From 2e0b26e032e6b305751df82af4c104c6620c0bd4 Mon Sep 17 00:00:00 2001
From: tian <11429339@qq.com>
Date: Wed, 1 Apr 2026 10:47:34 +0800
Subject: [PATCH] Refine rail assembly workflow and viewpoint strategies
---
AGENTS.md | 50 ++
NavisworksTransport.UnitTests.csproj | 2 +
TransportPlugin.csproj | 1 +
.../RailAssemblyWorkflowContextTests.cs | 118 +++
.../CoordinateSystem/ViewpointHelperTests.cs | 80 ++
doc/working/current-engineering-state.md | 91 +-
.../ViewModels/AnimationControlViewModel.cs | 4 +-
.../WPF/ViewModels/ModelSettingsViewModel.cs | 4 +-
src/UI/WPF/ViewModels/PathEditingViewModel.cs | 788 +++++++++++-------
.../ViewModels/RailAssemblyWorkflowContext.cs | 154 ++++
src/UI/WPF/Views/PathEditingView.xaml | 4 -
src/Utils/CollisionSceneHelper.cs | 2 +-
src/Utils/ViewpointHelper.cs | 276 ++++++
13 files changed, 1243 insertions(+), 331 deletions(-)
create mode 100644 UnitTests/CoordinateSystem/RailAssemblyWorkflowContextTests.cs
create mode 100644 UnitTests/CoordinateSystem/ViewpointHelperTests.cs
create mode 100644 src/UI/WPF/ViewModels/RailAssemblyWorkflowContext.cs
diff --git a/AGENTS.md b/AGENTS.md
index 0bc6113..8851250 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -177,6 +177,56 @@
目标是做到:只看名字,就能知道这个方向、轴、向量到底属于宿主、内部、资产,还是对象自身局部轴。
+### 4.1.3 对象局部轴业务映射
+
+以后关于 `Local...`、前进轴、up 轴、side 轴,统一优先使用这个说法:
+
+- **对象局部轴业务映射**
+ - 它不是第四套全局坐标系
+ - 也不是几何天然自带的“真理”
+ - 它表示:在当前业务链路里,我们准备把对象自身哪根局部轴解释成:
+ - `forward`
+ - `up`
+ - `side`
+
+这个概念的边界必须非常明确:
+
+- 它是**业务解释层**
+ - 不是宿主坐标系
+ - 不是内部 `Canonical Space`
+ - 不是插件资源资产坐标系
+- 它不能脱离上下文单独存在
+ - `up` 往往要结合宿主 `up` 语义来解释
+ - `forward` 往往取决于:
+ - fragment 代表姿态解释
+ - 资源默认轴约定
+ - 用户选择
+ - 当前路径类型
+
+为什么必须保留这层概念:
+
+- 路径姿态求解必须回答:
+ - 对象哪根局部轴去对齐路径 `forward`
+ - 对象哪根局部轴去对齐目标 `up`
+- 如果没有这层映射,下面这些都无法稳定定义:
+ - `Ground / Hoisting / Rail` 起点姿态
+ - 逐帧姿态
+ - `Rail` 角度修正
+ - 通行空间尺寸投影
+ - 终点原始姿态保持
+ - `Rail` 平移模式的终点原位搬运
+
+当前项目里的硬约束:
+
+- 不要把“对象局部轴业务映射”写成“对象自身天然就有 forward/up 定义”
+- 对真实物体:
+ - 不能简单把 `ModelItem.Transform` 当成可靠的局部轴真值
+ - 应优先通过 fragment 代表姿态 + `Fragment默认Up` 去解释
+- 对虚拟物体:
+ - 对象局部轴业务映射通常来自明确的资产轴约定
+- 如果只是讨论对象自身局部轴语义,允许继续使用 `Local...` 命名
+ - 但文档、日志、设计讨论里优先说“对象局部轴业务映射”
+
### 4.1.1 Quaternion / Rotation3D 固定定义
这是项目级硬约束,不允许在任何修复中重新猜测、重新验证或临时改口:
diff --git a/NavisworksTransport.UnitTests.csproj b/NavisworksTransport.UnitTests.csproj
index 4fefe8e..f26da39 100644
--- a/NavisworksTransport.UnitTests.csproj
+++ b/NavisworksTransport.UnitTests.csproj
@@ -76,6 +76,8 @@
+
+
diff --git a/TransportPlugin.csproj b/TransportPlugin.csproj
index bc517ad..3cd6540 100644
--- a/TransportPlugin.csproj
+++ b/TransportPlugin.csproj
@@ -284,6 +284,7 @@
+
diff --git a/UnitTests/CoordinateSystem/RailAssemblyWorkflowContextTests.cs b/UnitTests/CoordinateSystem/RailAssemblyWorkflowContextTests.cs
new file mode 100644
index 0000000..aeb2308
--- /dev/null
+++ b/UnitTests/CoordinateSystem/RailAssemblyWorkflowContextTests.cs
@@ -0,0 +1,118 @@
+using Autodesk.Navisworks.Api;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NavisworksTransport.UI.WPF.ViewModels;
+using System.Numerics;
+
+namespace NavisworksTransport.UnitTests.CoordinateSystem
+{
+ [TestClass]
+ public class RailAssemblyWorkflowContextTests
+ {
+ [TestMethod]
+ public void ResetEndFaceAnalysis_ClearsAnalysisStateAndSeedPoints()
+ {
+ var context = CreateContext();
+ context.HasEndFaceAnalysis = true;
+ context.EndFaceCenterPoint = new Point3D(1.0, 2.0, 3.0);
+ context.EndFaceNormal = new Vector3(1f, 0f, 0f);
+ context.EndFaceSeedPoints.Add(new Point3D(4.0, 5.0, 6.0));
+
+ context.ResetEndFaceAnalysis();
+
+ Assert.IsFalse(context.HasEndFaceAnalysis);
+ Assert.IsNull(context.EndFaceCenterPoint);
+ Assert.AreEqual(default(Vector3), context.EndFaceNormal);
+ Assert.AreEqual(0, context.EndFaceSeedPoints.Count);
+ }
+
+ [TestMethod]
+ public void ResetInstallationReference_RestoresDefaultOffsetAndClearsReferenceState()
+ {
+ var context = CreateContext();
+ context.HasInstallationReference = true;
+ context.InstallationPickPoint = new Point3D(1.0, 2.0, 3.0);
+ context.InstallationBaseAnchorPoint = new Point3D(4.0, 5.0, 6.0);
+ context.InstallationAnchorPoint = new Point3D(7.0, 8.0, 9.0);
+ context.InstallationPlaneNormal = new Vector3(0f, 1f, 0f);
+ context.InstallationPlaneSpanDirection = new Vector3(1f, 0f, 0f);
+ context.InstallationOffsetDistanceInMeters = 2.5;
+ context.InstallationReferenceRouteId = "route-1";
+ context.InstallationSeedPoints.Add(new Point3D(9.0, 8.0, 7.0));
+ context.AnchorVerticalOffsetInMeters = 3.5;
+
+ context.ResetInstallationReference();
+
+ Assert.IsFalse(context.HasInstallationReference);
+ Assert.IsNull(context.InstallationPickPoint);
+ Assert.IsNull(context.InstallationBaseAnchorPoint);
+ Assert.IsNull(context.InstallationAnchorPoint);
+ Assert.AreEqual(default(Vector3), context.InstallationPlaneNormal);
+ Assert.AreEqual(default(Vector3), context.InstallationPlaneSpanDirection);
+ Assert.AreEqual(0.0, context.InstallationOffsetDistanceInMeters, 1e-9);
+ Assert.IsNull(context.InstallationReferenceRouteId);
+ Assert.AreEqual(0, context.InstallationSeedPoints.Count);
+ Assert.AreEqual(0.25, context.AnchorVerticalOffsetInMeters, 1e-9);
+ }
+
+ [TestMethod]
+ public void ResetSession_ClearsTransientStateButPreservesConfiguration()
+ {
+ var context = CreateContext();
+ context.WorkflowMode = RailAssemblyWorkflowMode.EditSelectedRail;
+ context.TerminalObjectName = "箱体A";
+ context.TerminalObjectInfo = "info";
+ context.ReferenceRodLengthInMeters = 12.0;
+ context.ReferenceRodDiameterInMeters = 0.4;
+ context.SphereCenterX = 10.0;
+ context.SphereCenterY = 11.0;
+ context.SphereCenterZ = 12.0;
+ context.MountMode = RailMountMode.OverRail;
+ context.HasTerminalObject = true;
+ context.IsSelectingStartPoint = true;
+ context.IsSelectingEndFacePoints = true;
+ context.IsSelectingInstallationPoint = true;
+ context.StartPoint = new Point3D(1.0, 2.0, 3.0);
+ context.StartPointText = "(1,2,3)";
+ context.HasEndFaceAnalysis = true;
+ context.EndFaceCenterPoint = new Point3D(4.0, 5.0, 6.0);
+ context.HasInstallationReference = true;
+ context.InstallationReferenceRouteId = "route-2";
+ context.AnchorVerticalOffsetInMeters = 1.5;
+ context.EndFaceSeedPoints.Add(new Point3D(1.0, 0.0, 0.0));
+ context.InstallationSeedPoints.Add(new Point3D(0.0, 1.0, 0.0));
+
+ context.ResetSession();
+
+ Assert.AreEqual(RailAssemblyWorkflowMode.None, context.WorkflowMode);
+ Assert.IsFalse(context.HasTerminalObject);
+ Assert.IsFalse(context.IsSelectingStartPoint);
+ Assert.IsFalse(context.IsSelectingEndFacePoints);
+ Assert.IsFalse(context.IsSelectingInstallationPoint);
+ Assert.IsNull(context.TerminalObject);
+ Assert.IsNotNull(context.StartPoint);
+ Assert.AreEqual("未选择", context.TerminalObjectName);
+ Assert.AreEqual("请选择终点处已安装箱体", context.TerminalObjectInfo);
+ Assert.AreEqual("未选择", context.StartPointText);
+ Assert.IsFalse(context.HasEndFaceAnalysis);
+ Assert.IsFalse(context.HasInstallationReference);
+ Assert.AreEqual(0.25, context.AnchorVerticalOffsetInMeters, 1e-9);
+ Assert.AreEqual(12.0, context.ReferenceRodLengthInMeters, 1e-9);
+ Assert.AreEqual(0.4, context.ReferenceRodDiameterInMeters, 1e-9);
+ Assert.AreEqual(10.0, context.SphereCenterX, 1e-9);
+ Assert.AreEqual(11.0, context.SphereCenterY, 1e-9);
+ Assert.AreEqual(12.0, context.SphereCenterZ, 1e-9);
+ Assert.AreEqual(RailMountMode.OverRail, context.MountMode);
+ }
+
+ private static RailAssemblyWorkflowContext CreateContext()
+ {
+ return new RailAssemblyWorkflowContext(
+ defaultReferenceRodLengthInMeters: 20.0,
+ defaultReferenceRodDiameterInMeters: 0.3,
+ defaultAnchorVerticalOffsetInMeters: 0.25,
+ defaultSphereCenterX: 0.0,
+ defaultSphereCenterY: 0.0,
+ defaultSphereCenterZ: 0.0);
+ }
+ }
+}
diff --git a/UnitTests/CoordinateSystem/ViewpointHelperTests.cs b/UnitTests/CoordinateSystem/ViewpointHelperTests.cs
new file mode 100644
index 0000000..6858379
--- /dev/null
+++ b/UnitTests/CoordinateSystem/ViewpointHelperTests.cs
@@ -0,0 +1,80 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using NavisworksTransport.Utils;
+using System.Numerics;
+
+namespace NavisworksTransport.UnitTests.CoordinateSystem
+{
+ [TestClass]
+ public class ViewpointHelperTests
+ {
+ [TestMethod]
+ public void ResolvePathViewpointProfile_ShouldReturnExpectedDefaults()
+ {
+ var ground = ViewpointHelper.ResolvePathViewpointProfile(ViewpointHelper.ViewpointStrategy.PathGroundSelection);
+ var hoisting = ViewpointHelper.ResolvePathViewpointProfile(ViewpointHelper.ViewpointStrategy.PathHoistingSelection);
+ var rail = ViewpointHelper.ResolvePathViewpointProfile(ViewpointHelper.ViewpointStrategy.PathRailSelection);
+
+ Assert.AreEqual(1.0, ground.DistanceScale, 1e-9);
+ Assert.AreEqual(12.0, ground.MinDistanceMeters, 1e-9);
+
+ Assert.AreEqual(1.2, hoisting.DistanceScale, 1e-9);
+ Assert.AreEqual(8.0, hoisting.MinDistanceMeters, 1e-9);
+ Assert.AreEqual(22.0, hoisting.ElevationDegrees, 1e-9);
+
+ Assert.AreEqual(0.9, rail.DistanceScale, 1e-9);
+ Assert.AreEqual(6.0, rail.MinDistanceMeters, 1e-9);
+ Assert.AreEqual(12.0, rail.ElevationDegrees, 1e-9);
+ }
+
+ [TestMethod]
+ public void ResolveCameraOffsetDirection_Hoisting_ShouldLookFromBehindAndAbove()
+ {
+ Vector3 hostUp = Vector3.UnitZ;
+ Vector3 forward = Vector3.UnitX;
+
+ Vector3 offset = ViewpointHelper.ResolveCameraOffsetDirection(PathType.Hoisting, hostUp, forward);
+
+ Assert.IsTrue(offset.X < -0.8f);
+ Assert.IsTrue(offset.Z > 0.3f);
+ }
+
+ [TestMethod]
+ public void ResolveCameraOffsetDirection_Rail_ShouldLookFromSideAndSlightlyAbove()
+ {
+ Vector3 hostUp = Vector3.UnitZ;
+ Vector3 forward = Vector3.UnitX;
+
+ Vector3 offset = ViewpointHelper.ResolveCameraOffsetDirection(PathType.Rail, hostUp, forward);
+
+ Assert.AreEqual(0.0f, offset.X, 1e-5f);
+ Assert.IsTrue(offset.Y > 0.9f);
+ Assert.IsTrue(offset.Z > 0.15f);
+ }
+
+ [TestMethod]
+ public void ResolveHorizontalPathForward_ShouldFallbackWhenPathIsVertical()
+ {
+ Vector3 hostUp = Vector3.UnitZ;
+ Vector3 rawVertical = Vector3.UnitZ;
+ Vector3 fallback = Vector3.UnitY;
+
+ Vector3 resolved = ViewpointHelper.ResolveHorizontalPathForward(rawVertical, hostUp, fallback);
+
+ Assert.AreEqual(0.0f, resolved.Z, 1e-5f);
+ Assert.AreEqual(1.0f, resolved.Y, 1e-5f);
+ }
+
+ [TestMethod]
+ public void ResolveFocusViewpointProfile_ShouldCentralizeNamedFocusStrategies()
+ {
+ var modelFocus = ViewpointHelper.ResolveFocusViewpointProfile(ViewpointHelper.ViewpointStrategy.ModelFocus);
+ var collision = ViewpointHelper.ResolveFocusViewpointProfile(ViewpointHelper.ViewpointStrategy.CollisionCloseUp);
+
+ Assert.AreEqual(60.0, modelFocus.ViewAngleDegrees, 1e-9);
+ Assert.AreEqual(0.25, modelFocus.TargetViewRatio, 1e-9);
+
+ Assert.AreEqual(60.0, collision.ViewAngleDegrees, 1e-9);
+ Assert.AreEqual(0.25, collision.TargetViewRatio, 1e-9);
+ }
+ }
+}
diff --git a/doc/working/current-engineering-state.md b/doc/working/current-engineering-state.md
index 44e23ff..7869750 100644
--- a/doc/working/current-engineering-state.md
+++ b/doc/working/current-engineering-state.md
@@ -1,12 +1,18 @@
# 当前工程状态
-更新时间:2026-03-25
+更新时间:2026-04-01
## 1. 当前稳定状态
- `Rail` 路径主链路已稳定:
- `ZUp` 模型下,轨上/轨下路径的真实物体与虚拟物体起点、动画、终点贴合正确。
- `YUp` 模型下,终端安装仿真、`Rail` 姿态、真实物体与虚拟物体通行空间、起点与动画主链路已基本跑通。
+ - `Rail` “调整角度”窗口中的“平移”模式当前已重新收稳:
+ - 以终点箱体当前原始位姿为真值;
+ - 记录终点原位相对路径终点参考点的固定残差;
+ - 把这份残差整体搬运到路径起点;
+ - 动画全程保持终点原始姿态;
+ - 动画结束时应精确回到终点箱体原位。
- 地面路径在 `YUp` 模型下:
- 虚拟物体起点姿态、动画姿态、转弯姿态已恢复正常。
- 真实物体地面路径当前已恢复到可用状态:
@@ -20,16 +26,24 @@
- 对象级前进轴统一按 `PositiveX` 解释;
- 不再因为路径初始方向更偏 `Z` 就把对象前进轴自动切换成 `PositiveZ`。
- 当前已禁止地面/吊装路径偷偷退回旧 `yaw` 链路。
+- 吊装路径真实物体的角度调整链路已重新稳定:
+ - 起点“调整角度”后,预览、生成动画、播放前重置都复用同一份真实起点基姿态;
+ - 清除并重新选择物体时,旧角度修正必须被清空,不能继承上一次调整状态。
- 碰撞检测/恢复主链路已稳定:
- `ClashDetective` 三维恢复不能再先 `ResetPermanentTransform`。
- 碰撞恢复、自动报告、自动截图已重新对齐到动画主链路。
- 虚拟物体资源问题已确认并修复:
- 旧 `unit_cube.nwc` 局部几何中心不在原点,会导致虚拟物体中心偏差。
- 新 `unit_cube.nwc` 已替换为几何中心在原点的版本。
- - 旋转适配入口当前稳定分流:
- - 虚拟物体继续走 `HostCoordinateAdapter` 的 `Legacy` 入口;
- - 真实物体走 `Direct` 入口;
- - 两者不能再强行共用同一条旋转转换链。
+- 旋转适配入口当前稳定分流:
+ - 虚拟物体继续走 `HostCoordinateAdapter` 的 `Legacy` 入口;
+ - 真实物体走 `Direct` 入口;
+ - 两者不能再强行共用同一条旋转转换链。
+- `Rail` 终端安装仿真创建/编辑区当前已完成第一轮工作流拆分:
+ - 新建 Rail 与编辑选中 Rail 不再共用同一套按钮命令入口;
+ - 新建区和编辑区有独立的 `找端面 / 选安装点 / 取起点` 可用条件;
+ - 两个区域都已有明确的“取消装配”退出入口;
+ - 上方“路径编辑 -> 结束”在 Rail 装配工作流活跃时,也必须能真正退出 Rail 装配流程。
## 2. 当前坐标系架构
@@ -212,6 +226,19 @@
2. 再验证修正后 `forward` 是否仍沿 rail 切向
3. 最后再看 Navisworks 应用层是否正确按三步增量姿态落位
+### 4.7.1 Rail 平移模式
+
+- `Rail` 的“平移”不是重新按轨上/轨下求一套新的安装姿态。
+- 当前稳定语义:
+ - 终点箱体当前原始位姿是唯一真值;
+ - 先计算终点箱体相对“路径终点参考点”的固定残差;
+ - 再把这份残差搬运到路径起点;
+ - 动画全程保持这套终点原始姿态和固定残差。
+- 当前规则:
+ - 不能把“平移模式”误实现成重新按 `RailMountMode` 解目标 pose;
+ - 不能锁到“参考姿态”而丢掉终点箱体当前真实几何姿态;
+ - 终点保位误差如果异常,优先检查“残差是相对终点参考点采样,还是误相对起点参考点采样”。
+
### 4.8 Rail 真实物体必须复用统一姿态解释层
- `Rail` 不应再单独退回默认 `PositiveX / PositiveY` 轴约定。
@@ -243,6 +270,40 @@
- `AnimationControlViewModel.UpdatePassageSpaceVisualization()` 中,真实物体的 `Rail` 和 `Hoisting` 必须分开消费
- `Rail` 真实物体不能再复用 `Hoisting` 的法向/分段参数语义
+### 4.10 Rail 终端安装仿真工作流
+
+- 当前真实问题已经确认:
+ - “创建 Rail 路径”区和“Rail 参数”编辑区之前共用同一套装配状态、命令入口和按钮可用条件;
+ - 导致新建流程会误吃编辑态校验,或在切换路径后残留旧的装配状态。
+- 当前第一轮稳定修复:
+ - 新建 Rail 与编辑 Rail 已拆成独立命令入口;
+ - “找端面”在新建 Rail 时不再要求先选中一条现有 Rail 路径;
+ - 新建区的“选安装点”不再受 `IsRailRouteSelected` 编辑态条件限制;
+ - 安装点拾取完成后,拾取点、安装点、安装参考面、中心线必须保留可视化,不能在收尾时被一并清掉;
+ - “隐藏辅助线”必须同时清理:
+ - 参考杆与锚点
+ - 球心到光轴参考点基准线
+ - 端面分析可视化
+ - 安装点、安装中心线、安装参考面
+ - 新建区和编辑区都必须有明确的“取消装配”;
+ - “路径编辑 -> 结束”在 Rail 装配流程活跃时,也必须能触发同一条取消/清理链路;
+ - 切换路径时必须自动清空旧的 Rail 装配上下文,不能把旧状态带到下一条路径。
+- 当前仍要记住:
+ - 这块只是“第一轮工作流拆分”,还不是彻底的数据结构重构;
+ - 如果后续继续扩 Rail 终端安装仿真,优先方向应是把“创建态上下文”和“编辑态上下文”进一步拆成独立 context,而不是继续在 ViewModel 顶层堆共享字段和布尔分支。
+
+### 4.11 吊装路径角度调整
+
+- 当前真实根因已经确认:
+ - 吊装真实物体在“起点调整角度后生成动画”时,如果基姿态缓存不跟着角度修正一起重建,就会出现:
+ - 动画里没有沿用调整后的角度;
+ - 甚至被错误立起或只抬高一点的异常现象。
+- 当前稳定规则:
+ - 吊装路径必须缓存“真实起点基姿态 + 起始 yaw”;
+ - 起点预览、动画生成、播放前重置,都要复用同一份基线;
+ - 只要角度修正变化,就必须清掉旧缓存并重新 `MoveObjectToPathStart()`;
+ - 清除物体或重新选择物体时,角度修正状态必须归零,不允许继承上一轮对象的角度状态。
+
## 5. 当前保留的日志策略
- 保留:
@@ -257,8 +318,16 @@
- 已降级或删除:
- 大量重复逐帧宿主姿态轴日志
- 虚拟物体 `Transform` 即时读回日志(容易误导)
+ - `Hoisting` 真实物体起点姿态与平面前进轴的刷屏 `Info` 日志,已降为 `Debug`
-## 6. 当前 Navisworks 变换 API 结论
+## 6. 最近关键提交
+
+- `c3b103c` `Add explicit exit for rail assembly workflow`
+- `c974469` `Separate rail creation and edit assembly flows`
+- `d4c49fc` `Stabilize hoisting pose adjustment flow`
+- `cb56737` `Preserve terminal pose when translating rail objects to path start`
+
+## 7. 当前 Navisworks 变换 API 结论
- `ModelItem.Transform`
- 只表示原始设计文件变换;
@@ -279,13 +348,13 @@
- 清掉的是永久增量层;
- 不会修改原始设计文件变换。
-## 6. 当前还值得继续观察的点
+## 8. 当前还值得继续观察的点
- 空轨路径里仍有一个旧 warning:
- `[空轨] 双轨几何中心线提取失败,退回 OBB 主轴中线`
- 这与当前 `YUp` 地面路径问题无关,但后续可以单独处理。
-## 7. 下一步建议
+## 9. 下一步建议
- 继续按“先框架、先测试、后接业务”的方式推进。
- 新问题优先:
@@ -293,8 +362,12 @@
2. 若日志不足,先补日志
3. 再补针对性的回归测试
4. 最后再改业务代码
+- `Rail` 终端安装仿真的下一轮优化优先级:
+ 1. 把创建态/编辑态从 ViewModel 顶层共享字段继续拆成独立 context
+ 2. 为“取消装配 / 切换路径 / 中途退出后重新开始”补单元测试或更轻量的状态回归验证
+ 3. 再考虑是否需要把创建区与编辑区在 UI 上做更明确的视觉分隔
-## 8. 当前阶段的工作边界
+## 10. 当前阶段的工作边界
- 不要回到“直接补旧 `yaw` 链路”的方式。
- 不要再增加隐藏错误的 fallback。
diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
index 7dfc8b4..4689bb8 100644
--- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
+++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
@@ -2458,7 +2458,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
ModelHighlightHelper.HighlightItems(ManualTargetsHighlightCategory, items);
// 聚焦到对象(斜上方45度视角)
- ViewpointHelper.FocusOnModelItem(target.ModelItem, viewAngleDegrees: 60.0, targetViewRatio: 0.25);
+ ViewpointHelper.FocusOnModelItem(target.ModelItem, ViewpointHelper.ViewpointStrategy.ModelFocus);
UpdateMainStatus($"已聚焦到: {target.DisplayName}");
LogManager.Info($"聚焦到手工指定对象: {target.DisplayName}");
@@ -2484,7 +2484,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
ModelHighlightHelper.HighlightItems(ExcludedObjectsHighlightCategory, items);
// 聚焦到对象(斜上方45度视角)
- ViewpointHelper.FocusOnModelItem(excludedObject.ModelItem, viewAngleDegrees: 60.0, targetViewRatio: 0.25);
+ ViewpointHelper.FocusOnModelItem(excludedObject.ModelItem, ViewpointHelper.ViewpointStrategy.ModelFocus);
UpdateMainStatus($"已聚焦到排除对象: {excludedObject.DisplayName}");
LogManager.Info($"聚焦到排除对象: {excludedObject.DisplayName}");
diff --git a/src/UI/WPF/ViewModels/ModelSettingsViewModel.cs b/src/UI/WPF/ViewModels/ModelSettingsViewModel.cs
index 1e0093e..7eb10bd 100644
--- a/src/UI/WPF/ViewModels/ModelSettingsViewModel.cs
+++ b/src/UI/WPF/ViewModels/ModelSettingsViewModel.cs
@@ -1597,7 +1597,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
try
{
// 聚焦到模型(斜上方60度视角)
- ViewpointHelper.FocusOnModelItem(logisticsModel.NavisworksItem, viewAngleDegrees: 60.0, targetViewRatio: 0.25);
+ ViewpointHelper.FocusOnModelItem(logisticsModel.NavisworksItem, ViewpointHelper.ViewpointStrategy.ModelFocus);
UpdateMainStatus($"已聚焦到物流模型: {logisticsModel.Name}");
LogManager.Info($"聚焦到物流模型: {logisticsModel.Name}, 类别: {logisticsModel.Category}");
@@ -1684,4 +1684,4 @@ namespace NavisworksTransport.UI.WPF.ViewModels
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs
index 2db19f5..5370876 100644
--- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs
+++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs
@@ -71,13 +71,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
///
public class PathEditingViewModel : ViewModelBase, IDisposable
{
- private enum RailAssemblyWorkflowMode
- {
- None,
- CreateNewRailPath,
- EditSelectedRail
- }
-
#region 私有字段
private PathPlanningManager _pathPlanningManager;
@@ -135,38 +128,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private const double DefaultAssemblySphereCenterX = 0.0;
private const double DefaultAssemblySphereCenterY = 0.0;
private const double DefaultAssemblySphereCenterZ = 0.0;
- private string _assemblyTerminalObjectName = "未选择";
- private string _assemblyTerminalObjectInfo = "请选择终点处已安装箱体";
- private double _assemblyReferenceRodLengthInMeters = DefaultAssemblyReferenceRodLengthInMeters;
- private double _assemblyReferenceRodDiameterInMeters = DefaultAssemblyReferenceRodDiameterInMeters;
- private double _assemblyAnchorVerticalOffsetInMeters = DefaultAssemblyAnchorVerticalOffsetInMeters;
- private double _assemblySphereCenterX = DefaultAssemblySphereCenterX;
- private double _assemblySphereCenterY = DefaultAssemblySphereCenterY;
- private double _assemblySphereCenterZ = DefaultAssemblySphereCenterZ;
- private string _assemblyStartPointText = "未选择";
- private RailMountMode _assemblyMountMode = RailMountMode.UnderRail;
- private bool _hasAssemblyTerminalObject;
- private bool _isSelectingAssemblyStartPoint;
- private bool _isEditingSelectedRailStartPoint;
- private bool _isSelectingAssemblyEndFacePoints;
- private bool _isSelectingAssemblyInstallationPoint;
- private bool _isEditingSelectedRailInstallationPoint;
- private RailAssemblyWorkflowMode _railAssemblyWorkflowMode = RailAssemblyWorkflowMode.None;
- private bool _hasAssemblyEndFaceAnalysis;
- private bool _hasAssemblyInstallationReference;
- private ModelItem _assemblyTerminalObject;
- private Point3D _assemblyStartPoint;
- private Point3D _assemblyEndFaceCenterPoint;
- private Vector3 _assemblyEndFaceNormal;
- private Point3D _assemblyInstallationPickPoint;
- private Point3D _assemblyInstallationBaseAnchorPoint;
- private Point3D _assemblyInstallationAnchorPoint;
- private Vector3 _assemblyInstallationPlaneNormal;
- private Vector3 _assemblyInstallationPlaneSpanDirection;
- private double _assemblyInstallationOffsetDistanceInMeters;
- private string _assemblyInstallationReferenceRouteId;
- private readonly List _assemblyInstallationSeedPoints = new List();
- private readonly List _assemblyEndFaceSeedPoints = new List();
+ private readonly RailAssemblyWorkflowContext _railAssemblyContext = new RailAssemblyWorkflowContext(
+ DefaultAssemblyReferenceRodLengthInMeters,
+ DefaultAssemblyReferenceRodDiameterInMeters,
+ DefaultAssemblyAnchorVerticalOffsetInMeters,
+ DefaultAssemblySphereCenterX,
+ DefaultAssemblySphereCenterY,
+ DefaultAssemblySphereCenterZ);
private const string AssemblyAnchorMarkerPathId = "assembly_anchor_marker";
private const string AssemblyCenterGuideLinePathId = "assembly_center_guide_line";
private const string AssemblyReferenceLinePathId = "assembly_reference_line";
@@ -181,6 +149,178 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private const double AssemblyInstallationPlanePaddingInMeters = 0.5;
private const double AssemblyInstallationLineLengthScale = 1.2;
+ private string _assemblyTerminalObjectName
+ {
+ get => _railAssemblyContext.TerminalObjectName;
+ set => _railAssemblyContext.TerminalObjectName = value;
+ }
+
+ private string _assemblyTerminalObjectInfo
+ {
+ get => _railAssemblyContext.TerminalObjectInfo;
+ set => _railAssemblyContext.TerminalObjectInfo = value;
+ }
+
+ private double _assemblyReferenceRodLengthInMeters
+ {
+ get => _railAssemblyContext.ReferenceRodLengthInMeters;
+ set => _railAssemblyContext.ReferenceRodLengthInMeters = value;
+ }
+
+ private double _assemblyReferenceRodDiameterInMeters
+ {
+ get => _railAssemblyContext.ReferenceRodDiameterInMeters;
+ set => _railAssemblyContext.ReferenceRodDiameterInMeters = value;
+ }
+
+ private double _assemblyAnchorVerticalOffsetInMeters
+ {
+ get => _railAssemblyContext.AnchorVerticalOffsetInMeters;
+ set => _railAssemblyContext.AnchorVerticalOffsetInMeters = value;
+ }
+
+ private double _assemblySphereCenterX
+ {
+ get => _railAssemblyContext.SphereCenterX;
+ set => _railAssemblyContext.SphereCenterX = value;
+ }
+
+ private double _assemblySphereCenterY
+ {
+ get => _railAssemblyContext.SphereCenterY;
+ set => _railAssemblyContext.SphereCenterY = value;
+ }
+
+ private double _assemblySphereCenterZ
+ {
+ get => _railAssemblyContext.SphereCenterZ;
+ set => _railAssemblyContext.SphereCenterZ = value;
+ }
+
+ private string _assemblyStartPointText
+ {
+ get => _railAssemblyContext.StartPointText;
+ set => _railAssemblyContext.StartPointText = value;
+ }
+
+ private RailMountMode _assemblyMountMode
+ {
+ get => _railAssemblyContext.MountMode;
+ set => _railAssemblyContext.MountMode = value;
+ }
+
+ private bool _hasAssemblyTerminalObject
+ {
+ get => _railAssemblyContext.HasTerminalObject;
+ set => _railAssemblyContext.HasTerminalObject = value;
+ }
+
+ private bool _isSelectingAssemblyStartPoint
+ {
+ get => _railAssemblyContext.IsSelectingStartPoint;
+ set => _railAssemblyContext.IsSelectingStartPoint = value;
+ }
+
+ private bool _isSelectingAssemblyEndFacePoints
+ {
+ get => _railAssemblyContext.IsSelectingEndFacePoints;
+ set => _railAssemblyContext.IsSelectingEndFacePoints = value;
+ }
+
+ private bool _isSelectingAssemblyInstallationPoint
+ {
+ get => _railAssemblyContext.IsSelectingInstallationPoint;
+ set => _railAssemblyContext.IsSelectingInstallationPoint = value;
+ }
+
+ private RailAssemblyWorkflowMode _railAssemblyWorkflowMode
+ {
+ get => _railAssemblyContext.WorkflowMode;
+ set => _railAssemblyContext.WorkflowMode = value;
+ }
+
+ private bool _hasAssemblyEndFaceAnalysis
+ {
+ get => _railAssemblyContext.HasEndFaceAnalysis;
+ set => _railAssemblyContext.HasEndFaceAnalysis = value;
+ }
+
+ private bool _hasAssemblyInstallationReference
+ {
+ get => _railAssemblyContext.HasInstallationReference;
+ set => _railAssemblyContext.HasInstallationReference = value;
+ }
+
+ private ModelItem _assemblyTerminalObject
+ {
+ get => _railAssemblyContext.TerminalObject;
+ set => _railAssemblyContext.TerminalObject = value;
+ }
+
+ private Point3D _assemblyStartPoint
+ {
+ get => _railAssemblyContext.StartPoint;
+ set => _railAssemblyContext.StartPoint = value;
+ }
+
+ private Point3D _assemblyEndFaceCenterPoint
+ {
+ get => _railAssemblyContext.EndFaceCenterPoint;
+ set => _railAssemblyContext.EndFaceCenterPoint = value;
+ }
+
+ private Vector3 _assemblyEndFaceNormal
+ {
+ get => _railAssemblyContext.EndFaceNormal;
+ set => _railAssemblyContext.EndFaceNormal = value;
+ }
+
+ private Point3D _assemblyInstallationPickPoint
+ {
+ get => _railAssemblyContext.InstallationPickPoint;
+ set => _railAssemblyContext.InstallationPickPoint = value;
+ }
+
+ private Point3D _assemblyInstallationBaseAnchorPoint
+ {
+ get => _railAssemblyContext.InstallationBaseAnchorPoint;
+ set => _railAssemblyContext.InstallationBaseAnchorPoint = value;
+ }
+
+ private Point3D _assemblyInstallationAnchorPoint
+ {
+ get => _railAssemblyContext.InstallationAnchorPoint;
+ set => _railAssemblyContext.InstallationAnchorPoint = value;
+ }
+
+ private Vector3 _assemblyInstallationPlaneNormal
+ {
+ get => _railAssemblyContext.InstallationPlaneNormal;
+ set => _railAssemblyContext.InstallationPlaneNormal = value;
+ }
+
+ private Vector3 _assemblyInstallationPlaneSpanDirection
+ {
+ get => _railAssemblyContext.InstallationPlaneSpanDirection;
+ set => _railAssemblyContext.InstallationPlaneSpanDirection = value;
+ }
+
+ private double _assemblyInstallationOffsetDistanceInMeters
+ {
+ get => _railAssemblyContext.InstallationOffsetDistanceInMeters;
+ set => _railAssemblyContext.InstallationOffsetDistanceInMeters = value;
+ }
+
+ private string _assemblyInstallationReferenceRouteId
+ {
+ get => _railAssemblyContext.InstallationReferenceRouteId;
+ set => _railAssemblyContext.InstallationReferenceRouteId = value;
+ }
+
+ private List _assemblyInstallationSeedPoints => _railAssemblyContext.InstallationSeedPoints;
+
+ private List _assemblyEndFaceSeedPoints => _railAssemblyContext.EndFaceSeedPoints;
+
// 自动路径起点和终点的路径对象引用(用于正确的ID管理)
private PathRoute _autoPathStartPointRoute = null;
private PathRoute _autoPathEndPointRoute = null;
@@ -250,8 +390,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
try
{
- ViewpointHelper.AdjustViewpointToPathCenter(coreRoute);
- LogManager.Info($"UI路径切换:已自动调整视角到路径中心: {value.Name}");
+ ViewpointHelper.AdjustViewpointForPath(coreRoute);
+ LogManager.Info($"UI路径切换:已自动调整视角: {value.Name}, 类型={coreRoute.PathType}");
}
catch (Exception ex)
{
@@ -417,13 +557,31 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public string AssemblyTerminalObjectName
{
get => _assemblyTerminalObjectName;
- set => SetProperty(ref _assemblyTerminalObjectName, value);
+ set
+ {
+ if (string.Equals(_assemblyTerminalObjectName, value, StringComparison.Ordinal))
+ {
+ return;
+ }
+
+ _assemblyTerminalObjectName = value;
+ OnPropertyChanged(nameof(AssemblyTerminalObjectName));
+ }
}
public string AssemblyTerminalObjectInfo
{
get => _assemblyTerminalObjectInfo;
- set => SetProperty(ref _assemblyTerminalObjectInfo, value);
+ set
+ {
+ if (string.Equals(_assemblyTerminalObjectInfo, value, StringComparison.Ordinal))
+ {
+ return;
+ }
+
+ _assemblyTerminalObjectInfo = value;
+ OnPropertyChanged(nameof(AssemblyTerminalObjectInfo));
+ }
}
public double AssemblyReferenceRodLengthInMeters
@@ -431,10 +589,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _assemblyReferenceRodLengthInMeters;
set
{
- if (SetProperty(ref _assemblyReferenceRodLengthInMeters, value))
+ if (Math.Abs(_assemblyReferenceRodLengthInMeters - value) < 1e-9)
{
- OnPropertyChanged(nameof(CanGenerateAssemblyReferenceRod));
+ return;
}
+
+ _assemblyReferenceRodLengthInMeters = value;
+ OnPropertyChanged(nameof(AssemblyReferenceRodLengthInMeters));
}
}
@@ -443,10 +604,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _assemblyReferenceRodDiameterInMeters;
set
{
- if (SetProperty(ref _assemblyReferenceRodDiameterInMeters, value))
+ if (Math.Abs(_assemblyReferenceRodDiameterInMeters - value) < 1e-9)
{
- OnPropertyChanged(nameof(CanGenerateAssemblyReferenceRod));
+ return;
}
+
+ _assemblyReferenceRodDiameterInMeters = value;
+ OnPropertyChanged(nameof(AssemblyReferenceRodDiameterInMeters));
}
}
@@ -455,7 +619,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _assemblyAnchorVerticalOffsetInMeters;
set
{
- if (SetProperty(ref _assemblyAnchorVerticalOffsetInMeters, value) && HasAssemblyTerminalObject)
+ if (Math.Abs(_assemblyAnchorVerticalOffsetInMeters - value) < 1e-9)
+ {
+ return;
+ }
+
+ _assemblyAnchorVerticalOffsetInMeters = value;
+ OnPropertyChanged(nameof(AssemblyAnchorVerticalOffsetInMeters));
+
+ if (HasAssemblyTerminalObject)
{
var coreRoute = GetSelectedCoreRoute();
if (_hasAssemblyInstallationReference)
@@ -475,7 +647,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
else
{
- RenderAssemblyAnchorMarker();
RefreshAssemblyReferenceRodIfNeeded();
}
}
@@ -487,7 +658,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _assemblySphereCenterX;
set
{
- if (SetProperty(ref _assemblySphereCenterX, value) && HasAssemblyTerminalObject)
+ if (Math.Abs(_assemblySphereCenterX - value) < 1e-9)
+ {
+ return;
+ }
+
+ _assemblySphereCenterX = value;
+ OnPropertyChanged(nameof(AssemblySphereCenterX));
+
+ if (HasAssemblyTerminalObject)
{
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
@@ -500,7 +679,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _assemblySphereCenterY;
set
{
- if (SetProperty(ref _assemblySphereCenterY, value) && HasAssemblyTerminalObject)
+ if (Math.Abs(_assemblySphereCenterY - value) < 1e-9)
+ {
+ return;
+ }
+
+ _assemblySphereCenterY = value;
+ OnPropertyChanged(nameof(AssemblySphereCenterY));
+
+ if (HasAssemblyTerminalObject)
{
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
@@ -513,7 +700,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _assemblySphereCenterZ;
set
{
- if (SetProperty(ref _assemblySphereCenterZ, value) && HasAssemblyTerminalObject)
+ if (Math.Abs(_assemblySphereCenterZ - value) < 1e-9)
+ {
+ return;
+ }
+
+ _assemblySphereCenterZ = value;
+ OnPropertyChanged(nameof(AssemblySphereCenterZ));
+
+ if (HasAssemblyTerminalObject)
{
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
@@ -524,13 +719,31 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public bool HasAssemblyTerminalObject
{
get => _hasAssemblyTerminalObject;
- set => SetProperty(ref _hasAssemblyTerminalObject, value);
+ set
+ {
+ if (_hasAssemblyTerminalObject == value)
+ {
+ return;
+ }
+
+ _hasAssemblyTerminalObject = value;
+ OnPropertyChanged(nameof(HasAssemblyTerminalObject));
+ }
}
public string AssemblyStartPointText
{
get => _assemblyStartPointText;
- set => SetProperty(ref _assemblyStartPointText, value);
+ set
+ {
+ if (string.Equals(_assemblyStartPointText, value, StringComparison.Ordinal))
+ {
+ return;
+ }
+
+ _assemblyStartPointText = value;
+ OnPropertyChanged(nameof(AssemblyStartPointText));
+ }
}
public RailMountMode AssemblyMountMode
@@ -538,7 +751,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _assemblyMountMode;
set
{
- if (SetProperty(ref _assemblyMountMode, value) && HasAssemblyTerminalObject)
+ if (_assemblyMountMode == value)
+ {
+ return;
+ }
+
+ _assemblyMountMode = value;
+ OnPropertyChanged(nameof(AssemblyMountMode));
+
+ if (HasAssemblyTerminalObject)
{
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
@@ -549,7 +770,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public bool IsSelectingAssemblyStartPoint
{
get => _isSelectingAssemblyStartPoint;
- set => SetProperty(ref _isSelectingAssemblyStartPoint, value);
+ set
+ {
+ if (_isSelectingAssemblyStartPoint == value)
+ {
+ return;
+ }
+
+ _isSelectingAssemblyStartPoint = value;
+ OnPropertyChanged(nameof(IsSelectingAssemblyStartPoint));
+ }
}
///
/// 更新路径可视化显示:清理其他路径,只显示当前选中的路径
@@ -1072,7 +1302,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public ICommand SaveAsPathCommand { get; private set; }
public ICommand CaptureAssemblyTerminalObjectForCreateCommand { get; private set; }
public ICommand ReCaptureAssemblyTerminalObjectForEditCommand { get; private set; }
- public ICommand GenerateAssemblyReferenceRodCommand { get; private set; }
public ICommand SelectAssemblyStartPointForCreateCommand { get; private set; }
public ICommand RepositionRailStartPointCommand { get; private set; }
public ICommand SelectAssemblyInstallationPointForCreateCommand { get; private set; }
@@ -1119,10 +1348,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
public bool CanExecuteExportPath => _pathPlanningManager?.Routes?.Count > 0 || SelectedPathRoute != null;
public bool CanExecuteSaveAsPath => SelectedPathRoute != null && SelectedPathRoute.Points.Count > 0;
- public bool CanGenerateAssemblyReferenceRod => HasAssemblyTerminalObject &&
- _hasAssemblyInstallationReference &&
- AssemblyReferenceRodLengthInMeters > 0 &&
- AssemblyReferenceRodDiameterInMeters > 0;
public bool CanSelectAssemblyStartPointForCreate => HasAssemblyTerminalObject &&
_pathPlanningManager != null &&
AssemblyReferencePathManager.Instance.HasReferenceLine &&
@@ -1182,6 +1407,28 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private bool IsRailCreateWorkflowActive => _railAssemblyWorkflowMode == RailAssemblyWorkflowMode.CreateNewRailPath;
private bool IsRailEditingWorkflowActive => _railAssemblyWorkflowMode == RailAssemblyWorkflowMode.EditSelectedRail;
+ private static bool IsRailAssemblyEditMode(RailAssemblyWorkflowMode workflowMode)
+ {
+ return workflowMode == RailAssemblyWorkflowMode.EditSelectedRail;
+ }
+
+ private static string GetRailAssemblyWorkflowLabel(RailAssemblyWorkflowMode workflowMode)
+ {
+ return IsRailAssemblyEditMode(workflowMode)
+ ? "编辑现有Rail路径"
+ : "新建Rail路径";
+ }
+
+ private bool ShouldShowAssemblyReferenceLineVisuals()
+ {
+ if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
+ {
+ return false;
+ }
+
+ return IsRailEditingWorkflowActive || _hasAssemblyInstallationReference;
+ }
+
public bool CanExecuteModifyPoint => SelectedPathPoint != null &&
_pathPlanningManager?.PathEditState != PathEditState.EditingPoint;
public bool CanExecuteCancelModifyPoint => _pathPlanningManager?.PathEditState == PathEditState.EditingPoint;
@@ -1718,7 +1965,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
SaveAsPathCommand = new RelayCommand(async () => await ExecuteSaveAsPathAsync(), () => CanExecuteSaveAsPath);
CaptureAssemblyTerminalObjectForCreateCommand = new RelayCommand(async () => await ExecuteCaptureAssemblyTerminalObjectAsync(RailAssemblyWorkflowMode.CreateNewRailPath));
ReCaptureAssemblyTerminalObjectForEditCommand = new RelayCommand(async () => await ExecuteCaptureAssemblyTerminalObjectAsync(RailAssemblyWorkflowMode.EditSelectedRail));
- GenerateAssemblyReferenceRodCommand = new RelayCommand(async () => await ExecuteGenerateAssemblyReferenceRodAsync(), () => CanGenerateAssemblyReferenceRod);
SelectAssemblyStartPointForCreateCommand = new RelayCommand(async () => await ExecuteSelectAssemblyStartPointAsync(), () => CanSelectAssemblyStartPointForCreate);
RepositionRailStartPointCommand = new RelayCommand(async () => await ExecuteRepositionRailStartPointAsync(), () => CanRepositionRailStartPoint);
SelectAssemblyInstallationPointForCreateCommand = new RelayCommand(async () => await ExecuteSelectAssemblyInstallationPointAsync(RailAssemblyWorkflowMode.CreateNewRailPath), () => CanSelectAssemblyInstallationPointForCreate);
@@ -1768,7 +2014,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
ClearAssemblyAnchorMarker();
ClearAssemblyEndFaceAnalysisVisuals();
ClearAssemblyInstallationReferenceVisuals();
- OnPropertyChanged(nameof(CanGenerateAssemblyReferenceRod));
NotifyRailAssemblyCommandStateChanged();
// 新建路径时隐藏其他路径的辅助线
@@ -1803,41 +2048,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
- private async Task ExecuteGenerateAssemblyReferenceRodAsync()
- {
- await SafeExecuteAsync(() =>
- {
- if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
- {
- throw new InvalidOperationException("终点箱体未设置或已失效,请重新捕获终点箱体");
- }
-
- AssemblyReferenceLine referenceLine = BuildAssemblyReferenceLine();
-
- AssemblyReferencePathManager.Instance.CreateOrUpdateReferenceRod(
- referenceLine.StartPoint,
- referenceLine.EndPoint,
- AssemblyReferenceRodDiameterInMeters);
-
- _assemblyStartPoint = Point3D.Origin;
- AssemblyStartPointText = "未选择";
- RefreshAssemblyTerminalObjectInfo(referenceLine.StartPoint, referenceLine.EndPoint);
- RenderAssemblyAnchorMarker();
- RenderAssemblyCenterGuideLine();
- RenderAssemblyReferenceLine(referenceLine);
- FocusOnAssemblyReferenceArea(referenceLine.StartPoint, referenceLine.EndPoint);
-
- UpdateMainStatus("已生成终端安装辅助线,请在三维视图中确认终点锚点、方向、外端位置和辅助线贴合情况");
- NotifyRailAssemblyCommandStateChanged();
- LogManager.Info(
- $"[直线装配] 已生成参考杆: {AssemblyTerminalObjectName}, " +
- $"终点锚点=({referenceLine.EndPoint.X:F2}, {referenceLine.EndPoint.Y:F2}, {referenceLine.EndPoint.Z:F2}), " +
- $"参考线外端=({referenceLine.StartPoint.X:F2}, {referenceLine.StartPoint.Y:F2}, {referenceLine.StartPoint.Z:F2}), " +
- $"方向=({referenceLine.Direction.X:F3}, {referenceLine.Direction.Y:F3}, {referenceLine.Direction.Z:F3}), " +
- $"资源={AssemblyReferencePathManager.Instance.ReferenceResourceName}");
- }, "生成终端安装辅助线");
- }
-
private async Task ExecuteSelectAssemblyStartPointAsync()
{
await SafeExecuteAsync(() =>
@@ -1854,27 +2064,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (!AssemblyReferencePathManager.Instance.HasReferenceLine)
{
- throw new InvalidOperationException("请先生成终端安装辅助线");
+ throw new InvalidOperationException("请先选择安装点,系统生成终端安装辅助线后才能取起点");
}
- CleanupAssemblyReferenceSelection();
-
- _pathPlanningManager.DisableMouseHandling();
- IsSelectingAssemblyStartPoint = true;
- NotifyRailAssemblyCommandStateChanged();
-
- PathClickToolPlugin.MouseClicked -= OnAssemblyReferenceMouseClicked;
- PathClickToolPlugin.MouseClicked += OnAssemblyReferenceMouseClicked;
-
- if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
- {
- CleanupAssemblyReferenceSelection();
- throw new InvalidOperationException("ToolPlugin 初始化失败,请重试");
- }
-
- _pathPlanningManager.StartClickTool(PathPointType.StartPoint);
- UpdateMainStatus("请在辅助线附近点击一个起点,系统会自动吸附到辅助线上并生成直线路径");
- LogManager.Info("[直线装配] 已进入起点拾取模式");
+ BeginAssemblyStartPointSelection(
+ statusMessage: "请在辅助线附近点击一个起点,系统会自动吸附到辅助线上并生成直线路径",
+ logMessage: "[直线装配] 已进入起点拾取模式",
+ toolPluginFailureMessage: "ToolPlugin 初始化失败,请重试");
}, "选择终端安装起点");
}
@@ -1905,25 +2101,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
throw new InvalidOperationException("当前 Rail 路径无法生成辅助线,请先检查路径起点和终点。");
}
- CleanupAssemblyReferenceSelection();
-
- _pathPlanningManager.DisableMouseHandling();
- _isEditingSelectedRailStartPoint = true;
- IsSelectingAssemblyStartPoint = true;
- NotifyRailAssemblyCommandStateChanged();
-
- PathClickToolPlugin.MouseClicked -= OnAssemblyReferenceMouseClicked;
- PathClickToolPlugin.MouseClicked += OnAssemblyReferenceMouseClicked;
-
- if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
- {
- CleanupAssemblyReferenceSelection();
- throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。");
- }
-
- _pathPlanningManager.StartClickTool(PathPointType.StartPoint);
- UpdateMainStatus("请在辅助线附近点击新的起点位置,系统会自动吸附到辅助线上并更新当前 Rail 路径。");
- LogManager.Info("[Rail构型] 已进入起点重选模式");
+ BeginAssemblyStartPointSelection(
+ statusMessage: "请在辅助线附近点击新的起点位置,系统会自动吸附到辅助线上并更新当前 Rail 路径。",
+ logMessage: "[Rail构型] 已进入起点重选模式",
+ toolPluginFailureMessage: "ToolPlugin 初始化失败,请重试。");
}, "重选 Rail 路径起点");
}
@@ -1957,45 +2138,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。");
}
- bool isEditingExistingRailRoute = workflowMode == RailAssemblyWorkflowMode.EditSelectedRail;
- var selectedRailRoute = isEditingExistingRailRoute ? GetSelectedCoreRoute() : null;
- if (isEditingExistingRailRoute)
- {
- if (selectedRailRoute == null || selectedRailRoute.PathType != PathType.Rail)
- {
- throw new InvalidOperationException("请先选中一条需要编辑安装点的 Rail 路径。");
- }
+ PrepareRailAssemblyWorkflowForMode(
+ workflowMode,
+ editActionDescription: "请先选中一条需要编辑安装点的 Rail 路径。",
+ syncMountModeFromRoute: true);
- AssemblyMountMode = selectedRailRoute.RailMountMode;
- }
-
- SetRailAssemblyWorkflowMode(workflowMode);
-
- CleanupAssemblyReferenceSelection();
- CleanupAssemblyEndFaceSelection(clearVisuals: false);
- CleanupAssemblyInstallationSelection();
- ClearAssemblyEndFaceAnalysisVisuals();
- ResetAssemblyEndFaceAnalysisState();
- ClearAssemblyInstallationReferenceVisuals();
- ResetAssemblyInstallationReferenceState();
- _assemblyEndFaceSeedPoints.Clear();
-
- _pathPlanningManager.DisableMouseHandling();
- _isSelectingAssemblyEndFacePoints = true;
- NotifyRailAssemblyCommandStateChanged();
-
- PathClickToolPlugin.MouseClicked -= OnAssemblyEndFaceMouseClicked;
- PathClickToolPlugin.MouseClicked += OnAssemblyEndFaceMouseClicked;
-
- if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
- {
- CleanupAssemblyEndFaceSelection(clearVisuals: false);
- throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。");
- }
-
- UpdateMainStatus("请在同一个端面平面上连续点击 3 个点,系统将分析端面中心。");
- LogManager.Info(
- $"[直线装配] 已进入端面三点分析模式,模式={(isEditingExistingRailRoute ? "编辑现有Rail路径" : "新建Rail路径")}");
+ BeginAssemblyEndFaceSelection(workflowMode);
}, "分析终端端面");
}
@@ -2013,39 +2161,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
throw new InvalidOperationException("终点箱体未设置或已失效,请先捕获终点箱体。");
}
- bool isEditingExistingRailRoute = workflowMode == RailAssemblyWorkflowMode.EditSelectedRail;
- var selectedRailRoute = isEditingExistingRailRoute ? GetSelectedCoreRoute() : null;
- if (isEditingExistingRailRoute && (selectedRailRoute == null || selectedRailRoute.PathType != PathType.Rail))
- {
- throw new InvalidOperationException("请先选中一条需要编辑安装点的 Rail 路径。");
- }
+ PrepareRailAssemblyWorkflowForMode(
+ workflowMode,
+ editActionDescription: "请先选中一条需要编辑安装点的 Rail 路径。",
+ syncMountModeFromRoute: false);
- SetRailAssemblyWorkflowMode(workflowMode);
-
- CleanupAssemblyReferenceSelection();
- CleanupAssemblyEndFaceSelection(clearVisuals: false);
- CleanupAssemblyInstallationSelection();
- ClearAssemblyInstallationReferenceVisuals();
-
- _pathPlanningManager.DisableMouseHandling();
- _isSelectingAssemblyInstallationPoint = true;
-
- _isEditingSelectedRailInstallationPoint = isEditingExistingRailRoute;
-
- NotifyRailAssemblyCommandStateChanged();
-
- PathClickToolPlugin.MouseClicked -= OnAssemblyInstallationMouseClicked;
- PathClickToolPlugin.MouseClicked += OnAssemblyInstallationMouseClicked;
-
- if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
- {
- CleanupAssemblyInstallationSelection();
- throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。");
- }
-
- _assemblyInstallationSeedPoints.Clear();
- UpdateMainStatus("请在终点箱体表面连续点击两个安装面点,系统将按两点和光轴计算安装参考面与安装点。");
- LogManager.Info("[直线装配] 已进入安装点拾取模式");
+ BeginAssemblyInstallationSelection();
}, "选择安装点");
}
@@ -2067,16 +2188,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
await SafeExecuteAsync(() =>
{
- if (_isEditingSelectedRailStartPoint)
- {
- UpdateSelectedRailRouteStartPoint(projectedStartPoint);
- UpdateMainStatus("已根据辅助线更新当前 Rail 路径起点");
- }
- else
- {
- CreateAssemblyLinearRoute(projectedStartPoint);
- UpdateMainStatus("已根据辅助线起点生成终端安装路径");
- }
+ ApplyAssemblyStartPointForCurrentWorkflow(projectedStartPoint);
CleanupAssemblyReferenceSelection();
}, "生成终端安装路径");
@@ -2163,22 +2275,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
_assemblyInstallationSeedPoints[0],
_assemblyInstallationSeedPoints[1]);
- if (_isEditingSelectedRailInstallationPoint)
- {
- // 编辑现有路径:更新安装点并同步起点
- var selectedRailRoute = GetSelectedCoreRoute();
- if (selectedRailRoute != null && selectedRailRoute.PathType == PathType.Rail)
- {
- PersistAssemblyInstallationReferenceToRailRoute(
- selectedRailRoute,
- "已重选安装点并同步更新 Rail 路径安装参数");
- }
- }
- else
- {
- // 创建新路径:生成参考线,等待用户选择起点
- LogManager.Info("[直线装配] 安装点已确定,请继续选择起点以生成新路径");
- }
+ ApplyAssemblyInstallationReferenceForCurrentWorkflow();
CleanupAssemblyInstallationSelection(clearVisuals: false);
}, "处理安装点拾取");
}
@@ -2190,6 +2287,138 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
+ private void PrepareRailAssemblyWorkflowForMode(
+ RailAssemblyWorkflowMode workflowMode,
+ string editActionDescription,
+ bool syncMountModeFromRoute)
+ {
+ PathRoute selectedRailRoute = null;
+ if (IsRailAssemblyEditMode(workflowMode))
+ {
+ selectedRailRoute = RequireSelectedRailRouteForAssemblyEdit(editActionDescription);
+ if (syncMountModeFromRoute)
+ {
+ AssemblyMountMode = selectedRailRoute.RailMountMode;
+ }
+ }
+
+ SetRailAssemblyWorkflowMode(workflowMode);
+ }
+
+ private PathRoute RequireSelectedRailRouteForAssemblyEdit(string errorMessage)
+ {
+ var selectedRailRoute = GetSelectedCoreRoute();
+ if (selectedRailRoute == null || selectedRailRoute.PathType != PathType.Rail)
+ {
+ throw new InvalidOperationException(errorMessage);
+ }
+
+ return selectedRailRoute;
+ }
+
+ private void BeginAssemblyStartPointSelection(string statusMessage, string logMessage, string toolPluginFailureMessage)
+ {
+ CleanupAssemblyReferenceSelection();
+
+ _pathPlanningManager.DisableMouseHandling();
+ IsSelectingAssemblyStartPoint = true;
+ NotifyRailAssemblyCommandStateChanged();
+
+ PathClickToolPlugin.MouseClicked -= OnAssemblyReferenceMouseClicked;
+ PathClickToolPlugin.MouseClicked += OnAssemblyReferenceMouseClicked;
+
+ if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
+ {
+ CleanupAssemblyReferenceSelection();
+ throw new InvalidOperationException(toolPluginFailureMessage);
+ }
+
+ _pathPlanningManager.StartClickTool(PathPointType.StartPoint);
+ UpdateMainStatus(statusMessage);
+ LogManager.Info(logMessage);
+ }
+
+ private void BeginAssemblyEndFaceSelection(RailAssemblyWorkflowMode workflowMode)
+ {
+ CleanupAssemblyReferenceSelection();
+ CleanupAssemblyEndFaceSelection(clearVisuals: false);
+ CleanupAssemblyInstallationSelection();
+ ClearAssemblyEndFaceAnalysisVisuals();
+ ResetAssemblyEndFaceAnalysisState();
+ ClearAssemblyInstallationReferenceVisuals();
+ ResetAssemblyInstallationReferenceState();
+ _assemblyEndFaceSeedPoints.Clear();
+
+ _pathPlanningManager.DisableMouseHandling();
+ _isSelectingAssemblyEndFacePoints = true;
+ NotifyRailAssemblyCommandStateChanged();
+
+ PathClickToolPlugin.MouseClicked -= OnAssemblyEndFaceMouseClicked;
+ PathClickToolPlugin.MouseClicked += OnAssemblyEndFaceMouseClicked;
+
+ if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
+ {
+ CleanupAssemblyEndFaceSelection(clearVisuals: false);
+ throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。");
+ }
+
+ UpdateMainStatus("请在同一个端面平面上连续点击 3 个点,系统将分析端面中心。");
+ LogManager.Info($"[直线装配] 已进入端面三点分析模式,模式={GetRailAssemblyWorkflowLabel(workflowMode)}");
+ }
+
+ private void BeginAssemblyInstallationSelection()
+ {
+ CleanupAssemblyReferenceSelection();
+ CleanupAssemblyEndFaceSelection(clearVisuals: false);
+ CleanupAssemblyInstallationSelection();
+ ClearAssemblyInstallationReferenceVisuals();
+
+ _pathPlanningManager.DisableMouseHandling();
+ _isSelectingAssemblyInstallationPoint = true;
+ NotifyRailAssemblyCommandStateChanged();
+
+ PathClickToolPlugin.MouseClicked -= OnAssemblyInstallationMouseClicked;
+ PathClickToolPlugin.MouseClicked += OnAssemblyInstallationMouseClicked;
+
+ if (!ForceReinitializeToolPlugin(subscribeToEvents: false))
+ {
+ CleanupAssemblyInstallationSelection();
+ throw new InvalidOperationException("ToolPlugin 初始化失败,请重试。");
+ }
+
+ _assemblyInstallationSeedPoints.Clear();
+ UpdateMainStatus("请在终点箱体表面连续点击两个安装面点,系统将按两点和光轴计算安装参考面与安装点。");
+ LogManager.Info($"[直线装配] 已进入安装点拾取模式,模式={GetRailAssemblyWorkflowLabel(_railAssemblyWorkflowMode)}");
+ }
+
+ private void ApplyAssemblyStartPointForCurrentWorkflow(Point3D projectedStartPoint)
+ {
+ if (IsRailEditingWorkflowActive)
+ {
+ UpdateSelectedRailRouteStartPoint(projectedStartPoint);
+ ClearAssemblyReferenceLineVisuals();
+ UpdateMainStatus("已根据辅助线更新当前 Rail 路径起点");
+ return;
+ }
+
+ CreateAssemblyLinearRoute(projectedStartPoint);
+ UpdateMainStatus("已根据辅助线起点生成终端安装路径");
+ }
+
+ private void ApplyAssemblyInstallationReferenceForCurrentWorkflow()
+ {
+ if (IsRailEditingWorkflowActive)
+ {
+ var selectedRailRoute = RequireSelectedRailRouteForAssemblyEdit("当前没有可更新安装点的 Rail 路径。");
+ PersistAssemblyInstallationReferenceToRailRoute(
+ selectedRailRoute,
+ "已重选安装点并同步更新 Rail 路径安装参数");
+ return;
+ }
+
+ LogManager.Info("[直线装配] 安装点已确定,请继续选择起点以生成新路径");
+ }
+
private void CreateAssemblyLinearRoute(Point3D startPoint)
{
Point3D endPoint = AssemblyReferencePathManager.Instance.ReferenceLineEnd;
@@ -2324,16 +2553,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
ClearAssemblyInstallationReferenceVisuals();
ResetAssemblyEndFaceAnalysisState();
ResetAssemblyInstallationReferenceState();
-
- _assemblyTerminalObject = null;
- _assemblyStartPoint = Point3D.Origin;
+ _railAssemblyContext.ResetSession();
HasAssemblyTerminalObject = false;
- AssemblyTerminalObjectName = "未选择";
- AssemblyTerminalObjectInfo = "请选择终点处已安装箱体";
- AssemblyStartPointText = "未选择";
+ AssemblyTerminalObjectName = _assemblyTerminalObjectName;
+ AssemblyTerminalObjectInfo = _assemblyTerminalObjectInfo;
+ AssemblyStartPointText = _assemblyStartPointText;
SetRailAssemblyWorkflowMode(RailAssemblyWorkflowMode.None);
- OnPropertyChanged(nameof(CanGenerateAssemblyReferenceRod));
NotifyRailAssemblyCommandStateChanged();
if (announce && !string.IsNullOrWhiteSpace(statusMessage))
@@ -2595,7 +2821,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private void RefreshAssemblyReferenceRodIfNeeded()
{
- if (!AssemblyReferencePathManager.Instance.HasReferenceLine || _assemblyTerminalObject == null)
+ if (!ShouldShowAssemblyReferenceLineVisuals())
+ {
+ ClearAssemblyReferenceLineVisuals();
+ return;
+ }
+
+ if (_assemblyTerminalObject == null)
{
return;
}
@@ -2672,61 +2904,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
return standardRadiusInMeters * 0.8;
}
- private void FocusOnAssemblyReferenceArea(Point3D startPoint, Point3D endPoint)
- {
- try
- {
- double minX = Math.Min(startPoint.X, endPoint.X);
- double minY = Math.Min(startPoint.Y, endPoint.Y);
- double minZ = Math.Min(startPoint.Z, endPoint.Z);
- double maxX = Math.Max(startPoint.X, endPoint.X);
- double maxY = Math.Max(startPoint.Y, endPoint.Y);
- double maxZ = Math.Max(startPoint.Z, endPoint.Z);
-
- if (_assemblyTerminalObject != null && ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
- {
- var terminalBounds = _assemblyTerminalObject.BoundingBox();
- minX = Math.Min(minX, terminalBounds.Min.X);
- minY = Math.Min(minY, terminalBounds.Min.Y);
- minZ = Math.Min(minZ, terminalBounds.Min.Z);
- maxX = Math.Max(maxX, terminalBounds.Max.X);
- maxY = Math.Max(maxY, terminalBounds.Max.Y);
- maxZ = Math.Max(maxZ, terminalBounds.Max.Z);
- }
-
- Point3D center = new Point3D(
- (minX + maxX) / 2.0,
- (minY + maxY) / 2.0,
- (minZ + maxZ) / 2.0);
-
- double targetSize = Math.Max(
- maxX - minX,
- Math.Max(maxY - minY, maxZ - minZ));
-
- targetSize = Math.Max(targetSize, UnitsConverter.ConvertFromMeters(1.0));
-
- ViewpointHelper.FocusOnPosition(center, targetSize, 45.0, 0.35);
-
- var selection = new List();
- if (_assemblyTerminalObject != null && ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
- {
- selection.Add(_assemblyTerminalObject);
- }
-
- var referenceRod = AssemblyReferencePathManager.Instance.CurrentReferenceRod;
- if (referenceRod != null)
- {
- selection.Add(referenceRod);
- }
-
- NavisworksSelectionHelper.SetModelSelection(selection);
- }
- catch (Exception ex)
- {
- LogManager.Warning($"[直线装配] 聚焦参考杆区域失败: {ex.Message}");
- }
- }
-
private void RenderAssemblyAnchorMarker()
{
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
@@ -2930,13 +3107,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
RefreshAssemblyTerminalObjectInfo();
RefreshAssemblyReferenceRodIfNeeded();
UpdateMainStatus(
- $"安装参考已计算:安装点=({_assemblyInstallationAnchorPoint.X:F2}, {_assemblyInstallationAnchorPoint.Y:F2}, {_assemblyInstallationAnchorPoint.Z:F2}),偏距={_assemblyInstallationOffsetDistanceInMeters:F3}m");
+ $"安装参考已计算:安装点=({_assemblyInstallationAnchorPoint.X:F2}, {_assemblyInstallationAnchorPoint.Y:F2}, {_assemblyInstallationAnchorPoint.Z:F2}),偏距={_assemblyInstallationOffsetDistanceInMeters:F3}m,可继续取起点");
LogManager.Info(
$"[直线装配] 安装参考已计算: 安装面点1=({firstPickPoint.X:F3}, {firstPickPoint.Y:F3}, {firstPickPoint.Z:F3}), " +
$"安装面点2=({secondPickPoint.X:F3}, {secondPickPoint.Y:F3}, {secondPickPoint.Z:F3}), " +
$"安装点=({_assemblyInstallationAnchorPoint.X:F3}, {_assemblyInstallationAnchorPoint.Y:F3}, {_assemblyInstallationAnchorPoint.Z:F3}), " +
$"偏距={_assemblyInstallationOffsetDistanceInMeters:F3}m, 平面法向=({result.PlaneNormal.X:F4}, {result.PlaneNormal.Y:F4}, {result.PlaneNormal.Z:F4})");
- OnPropertyChanged(nameof(CanGenerateAssemblyReferenceRod));
NotifyRailAssemblyCommandStateChanged();
}
@@ -3139,9 +3315,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private void ResetAssemblyEndFaceAnalysisState()
{
- _hasAssemblyEndFaceAnalysis = false;
- _assemblyEndFaceCenterPoint = null;
- _assemblyEndFaceNormal = default(Vector3);
+ _railAssemblyContext.ResetEndFaceAnalysis();
}
private void ClearAssemblyInstallationReferenceVisuals()
@@ -3161,18 +3335,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private void ResetAssemblyInstallationReferenceState()
{
- _hasAssemblyInstallationReference = false;
- _assemblyInstallationPickPoint = null;
- _assemblyInstallationBaseAnchorPoint = null;
- _assemblyInstallationAnchorPoint = null;
- _assemblyInstallationPlaneNormal = default(Vector3);
- _assemblyInstallationPlaneSpanDirection = default(Vector3);
- _assemblyInstallationOffsetDistanceInMeters = 0.0;
- _assemblyInstallationReferenceRouteId = null;
- _assemblyInstallationSeedPoints.Clear();
- _assemblyAnchorVerticalOffsetInMeters = DefaultAssemblyAnchorVerticalOffsetInMeters;
+ _railAssemblyContext.ResetInstallationReference();
OnPropertyChanged(nameof(AssemblyAnchorVerticalOffsetInMeters));
- OnPropertyChanged(nameof(CanGenerateAssemblyReferenceRod));
NotifyRailAssemblyCommandStateChanged();
}
@@ -3182,7 +3346,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
PathClickToolPlugin.MouseClicked -= OnAssemblyReferenceMouseClicked;
IsSelectingAssemblyStartPoint = false;
- _isEditingSelectedRailStartPoint = false;
_pathPlanningManager?.EnableMouseHandling();
_pathPlanningManager?.StopClickTool();
NotifyRailAssemblyCommandStateChanged();
@@ -3199,7 +3362,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
PathClickToolPlugin.MouseClicked -= OnAssemblyInstallationMouseClicked;
_isSelectingAssemblyInstallationPoint = false;
- _isEditingSelectedRailInstallationPoint = false;
_pathPlanningManager?.EnableMouseHandling();
_pathPlanningManager?.StopClickTool();
if (clearVisuals)
diff --git a/src/UI/WPF/ViewModels/RailAssemblyWorkflowContext.cs b/src/UI/WPF/ViewModels/RailAssemblyWorkflowContext.cs
new file mode 100644
index 0000000..f5c3262
--- /dev/null
+++ b/src/UI/WPF/ViewModels/RailAssemblyWorkflowContext.cs
@@ -0,0 +1,154 @@
+using System.Collections.Generic;
+using System.Numerics;
+using Autodesk.Navisworks.Api;
+
+namespace NavisworksTransport.UI.WPF.ViewModels
+{
+ internal enum RailAssemblyWorkflowMode
+ {
+ None,
+ CreateNewRailPath,
+ EditSelectedRail
+ }
+
+ internal sealed class RailAssemblyWorkflowContext
+ {
+ private const string DefaultTerminalObjectName = "未选择";
+ private const string DefaultTerminalObjectInfo = "请选择终点处已安装箱体";
+ private const string DefaultAssemblyStartPointText = "未选择";
+
+ private readonly double _defaultReferenceRodLengthInMeters;
+ private readonly double _defaultReferenceRodDiameterInMeters;
+ private readonly double _defaultAnchorVerticalOffsetInMeters;
+ private readonly double _defaultSphereCenterX;
+ private readonly double _defaultSphereCenterY;
+ private readonly double _defaultSphereCenterZ;
+
+ public RailAssemblyWorkflowContext(
+ double defaultReferenceRodLengthInMeters,
+ double defaultReferenceRodDiameterInMeters,
+ double defaultAnchorVerticalOffsetInMeters,
+ double defaultSphereCenterX,
+ double defaultSphereCenterY,
+ double defaultSphereCenterZ)
+ {
+ _defaultReferenceRodLengthInMeters = defaultReferenceRodLengthInMeters;
+ _defaultReferenceRodDiameterInMeters = defaultReferenceRodDiameterInMeters;
+ _defaultAnchorVerticalOffsetInMeters = defaultAnchorVerticalOffsetInMeters;
+ _defaultSphereCenterX = defaultSphereCenterX;
+ _defaultSphereCenterY = defaultSphereCenterY;
+ _defaultSphereCenterZ = defaultSphereCenterZ;
+
+ TerminalObjectName = DefaultTerminalObjectName;
+ TerminalObjectInfo = DefaultTerminalObjectInfo;
+ ReferenceRodLengthInMeters = _defaultReferenceRodLengthInMeters;
+ ReferenceRodDiameterInMeters = _defaultReferenceRodDiameterInMeters;
+ AnchorVerticalOffsetInMeters = _defaultAnchorVerticalOffsetInMeters;
+ SphereCenterX = _defaultSphereCenterX;
+ SphereCenterY = _defaultSphereCenterY;
+ SphereCenterZ = _defaultSphereCenterZ;
+ StartPointText = DefaultAssemblyStartPointText;
+ MountMode = RailMountMode.UnderRail;
+ InstallationSeedPoints = new List();
+ EndFaceSeedPoints = new List();
+ }
+
+ public RailAssemblyWorkflowMode WorkflowMode { get; set; }
+
+ public string TerminalObjectName { get; set; }
+
+ public string TerminalObjectInfo { get; set; }
+
+ public double ReferenceRodLengthInMeters { get; set; }
+
+ public double ReferenceRodDiameterInMeters { get; set; }
+
+ public double AnchorVerticalOffsetInMeters { get; set; }
+
+ public double SphereCenterX { get; set; }
+
+ public double SphereCenterY { get; set; }
+
+ public double SphereCenterZ { get; set; }
+
+ public string StartPointText { get; set; }
+
+ public RailMountMode MountMode { get; set; }
+
+ public bool HasTerminalObject { get; set; }
+
+ public bool IsSelectingStartPoint { get; set; }
+
+ public bool IsSelectingEndFacePoints { get; set; }
+
+ public bool IsSelectingInstallationPoint { get; set; }
+
+ public bool HasEndFaceAnalysis { get; set; }
+
+ public bool HasInstallationReference { get; set; }
+
+ public ModelItem TerminalObject { get; set; }
+
+ public Point3D StartPoint { get; set; }
+
+ public Point3D EndFaceCenterPoint { get; set; }
+
+ public Vector3 EndFaceNormal { get; set; }
+
+ public Point3D InstallationPickPoint { get; set; }
+
+ public Point3D InstallationBaseAnchorPoint { get; set; }
+
+ public Point3D InstallationAnchorPoint { get; set; }
+
+ public Vector3 InstallationPlaneNormal { get; set; }
+
+ public Vector3 InstallationPlaneSpanDirection { get; set; }
+
+ public double InstallationOffsetDistanceInMeters { get; set; }
+
+ public string InstallationReferenceRouteId { get; set; }
+
+ public List InstallationSeedPoints { get; }
+
+ public List EndFaceSeedPoints { get; }
+
+ public void ResetEndFaceAnalysis()
+ {
+ HasEndFaceAnalysis = false;
+ EndFaceCenterPoint = null;
+ EndFaceNormal = default(Vector3);
+ EndFaceSeedPoints.Clear();
+ }
+
+ public void ResetInstallationReference()
+ {
+ HasInstallationReference = false;
+ InstallationPickPoint = null;
+ InstallationBaseAnchorPoint = null;
+ InstallationAnchorPoint = null;
+ InstallationPlaneNormal = default(Vector3);
+ InstallationPlaneSpanDirection = default(Vector3);
+ InstallationOffsetDistanceInMeters = 0.0;
+ InstallationReferenceRouteId = null;
+ InstallationSeedPoints.Clear();
+ AnchorVerticalOffsetInMeters = _defaultAnchorVerticalOffsetInMeters;
+ }
+
+ public void ResetSession()
+ {
+ WorkflowMode = RailAssemblyWorkflowMode.None;
+ HasTerminalObject = false;
+ IsSelectingStartPoint = false;
+ IsSelectingEndFacePoints = false;
+ IsSelectingInstallationPoint = false;
+ TerminalObject = null;
+ StartPoint = Point3D.Origin;
+ TerminalObjectName = DefaultTerminalObjectName;
+ TerminalObjectInfo = DefaultTerminalObjectInfo;
+ StartPointText = DefaultAssemblyStartPointText;
+ ResetEndFaceAnalysis();
+ ResetInstallationReference();
+ }
+ }
+}
diff --git a/src/UI/WPF/Views/PathEditingView.xaml b/src/UI/WPF/Views/PathEditingView.xaml
index 4ea9e67..73e7b6d 100644
--- a/src/UI/WPF/Views/PathEditingView.xaml
+++ b/src/UI/WPF/Views/PathEditingView.xaml
@@ -488,10 +488,6 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
Command="{Binding SelectAssemblyInstallationPointForCreateCommand}"
Style="{StaticResource SecondaryButtonStyle}"
IsEnabled="{Binding CanSelectAssemblyInstallationPointForCreate}"/>
-