Finalize canonical rail coordinate integration
This commit is contained in:
parent
3fd184934b
commit
c53db7a6fd
@ -476,6 +476,33 @@ else
|
||||
- 碰撞恢复
|
||||
- 辅助线/通行空间渲染
|
||||
|
||||
阶段 2 的两个重要约束:
|
||||
|
||||
1. 真实物体物理尺寸必须固定
|
||||
|
||||
- 起点贴合、动画帧生成、通行空间尺寸、碰撞恢复
|
||||
- 必须共用同一份固定物理尺寸
|
||||
- 不允许在对象已经旋转后,再从当前世界 AABB 重新推导“真实高度”
|
||||
|
||||
否则会产生典型错误:
|
||||
|
||||
- 起点贴合正确
|
||||
- 动画第一帧立刻出现固定间隙
|
||||
|
||||
2. 渲染与业务计算必须同时改
|
||||
|
||||
- 不能只改参考点、中心点、偏移量
|
||||
- 还必须同步改渲染几何局部轴:
|
||||
- `right`
|
||||
- `up`
|
||||
- `normal`
|
||||
- `height axis`
|
||||
|
||||
否则会出现:
|
||||
|
||||
- 业务点位正确
|
||||
- 但通行空间/辅助杆/长方体仍按旧 `Z-up` 轴构造
|
||||
|
||||
### 阶段 3
|
||||
|
||||
再逐步改造:
|
||||
@ -485,6 +512,27 @@ else
|
||||
- 坡度分析
|
||||
- 旧二维路径辅助逻辑
|
||||
|
||||
### 部署约束
|
||||
|
||||
WPF 插件的最终部署,必须依赖完整主项目构建产物,而不应默认复用测试顺带生成的程序集。
|
||||
|
||||
原因:
|
||||
|
||||
- DLL 时间戳正确,不代表插件可运行
|
||||
- 如果 `TransportPlugin.g.resources` 不完整,Navisworks 在创建面板时仍会因缺少 `.baml` 崩溃
|
||||
|
||||
必须保证:
|
||||
|
||||
- 主项目完整构建成功
|
||||
- 关键视图资源已经编入程序集
|
||||
|
||||
最低检查集:
|
||||
|
||||
- `LogisticsControlPanel.baml`
|
||||
- `PathEditingView.baml`
|
||||
- `AnimationControlView.baml`
|
||||
- `LayerManagementView.baml`
|
||||
|
||||
## 9. 关键结论
|
||||
|
||||
本项目如果要长期稳定支持 `Y-up` 和 `Z-up` 项目,正确方向不是:
|
||||
|
||||
@ -2437,4 +2437,90 @@ DialogHelper.ShowDialog(new MyDialog());
|
||||
|
||||
---
|
||||
|
||||
## 18. 基础框架改造的开发顺序
|
||||
|
||||
### 原则
|
||||
|
||||
当功能涉及以下任一类基础语义时,禁止直接在业务代码里试错式修补:
|
||||
|
||||
- 坐标系
|
||||
- 三维姿态
|
||||
- 几何局部轴
|
||||
- 单位系统
|
||||
- 动画/碰撞恢复的定位基准
|
||||
|
||||
正确顺序必须是:
|
||||
|
||||
1. 先抽出框架层/数学层
|
||||
2. 先写最小可验证测试
|
||||
3. 测试通过后再接回业务模块
|
||||
|
||||
### 原因
|
||||
|
||||
这类问题一旦直接在业务链路里反复修补,最容易出现:
|
||||
|
||||
- 虚拟物体正确、真实物体错误
|
||||
- 起点正确、动画第一帧错误
|
||||
- 动画正确、碰撞恢复错误
|
||||
- 中心点正确、渲染几何局部轴错误
|
||||
|
||||
根因通常不是“又差一个补偿”,而是底层语义没有先被验证。
|
||||
|
||||
### 本项目实证经验
|
||||
|
||||
本项目在 Rail 三维姿态与坐标系改造中,采用以下顺序后明显更稳:
|
||||
|
||||
1. 先建立 `HostCoordinateAdapter / ModelAxisConvention / CanonicalRailPoseBuilder`
|
||||
2. 再写单元测试验证 `Y-up / Z-up`、局部 `forward/up`、四元数/线性姿态
|
||||
3. 最后再接入:
|
||||
- 终端安装仿真
|
||||
- Rail 姿态
|
||||
- 动画播放
|
||||
- 碰撞恢复
|
||||
|
||||
结论:
|
||||
|
||||
- 对基础语义改造,**测试先行接业务** 是推荐流程
|
||||
- 没有测试支撑时,不应在业务代码里靠补偿和 fallback 猜结果
|
||||
|
||||
### 额外经验 1:物体物理尺寸必须固定
|
||||
|
||||
对于真实物体动画,物体的物理尺寸一旦确定,就必须固定存储并重复使用:
|
||||
|
||||
- 起点贴合使用的尺寸
|
||||
- 动画帧预计算使用的尺寸
|
||||
- 通行空间尺寸语义
|
||||
- 碰撞恢复使用的尺寸
|
||||
|
||||
这些必须来自**同一份固定物理尺寸**,不能在动画过程中反复从“当前已旋转的 AABB”重新推导。
|
||||
|
||||
否则会出现典型问题:
|
||||
|
||||
- 起点贴合正确
|
||||
- 动画第一帧立刻拉开固定间隙
|
||||
|
||||
### 额外经验 2:部署必须校验 WPF 资源完整性
|
||||
|
||||
对 WPF 插件来说,仅校验 DLL 被复制成功还不够。
|
||||
|
||||
必须确认构建产物包含完整的 `.g.resources / .baml` 资源;否则会出现:
|
||||
|
||||
- 插件 DLL 存在
|
||||
- Navisworks 能加载程序集
|
||||
- 但一打开面板就因缺少 `*.baml` 崩溃
|
||||
|
||||
本项目已实际遇到:
|
||||
|
||||
- `TransportPlugin.dll` 成功部署
|
||||
- 但缺少 `LogisticsControlPanel.baml / PathEditingView.baml / AnimationControlView.baml`
|
||||
- 最终表现为“插件布局丢失”或“手工打开插件窗口即崩溃”
|
||||
|
||||
因此经验是:
|
||||
|
||||
- 单元测试顺带产出的 DLL 不能默认用于最终部署
|
||||
- 最终部署前应至少确认主项目完整构建成功
|
||||
- 必要时校验关键 WPF 视图资源是否真的编入程序集
|
||||
|
||||
---
|
||||
|
||||
*本文档将随着项目发展持续更新,确保设计指导的有效性和实用性。*
|
||||
|
||||
@ -240,6 +240,8 @@
|
||||
验收:
|
||||
|
||||
- Rail 虚拟物体与真实模型在 Y-up / Z-up 项目中的姿态语义一致
|
||||
- 真实物体起点贴合和动画第一帧使用同一份固定物理尺寸语义
|
||||
- 不再允许从“已旋转后的当前 AABB”重新推导 Rail 物体高度
|
||||
|
||||
### Task 7.1 明确模型局部轴约定
|
||||
|
||||
@ -282,6 +284,11 @@
|
||||
- Rail 碰撞报告恢复位置和动画实际姿态一致
|
||||
- 二维路径不被三维逻辑污染
|
||||
|
||||
实施提示:
|
||||
|
||||
- 对真实物体,碰撞恢复和动画播放必须共用同一份固定物理尺寸语义
|
||||
- 不允许恢复链路偷偷退回“重新读取当前 AABB 再算高度/底面”
|
||||
|
||||
### Task 9. 改造渲染层
|
||||
|
||||
文件:
|
||||
@ -308,6 +315,29 @@
|
||||
- `Normal` 是否仍偷用世界 `Z`
|
||||
- 不能只修改 `ApplyVerticalOffset(...)` 或中心点偏移,而保留旧的 `XY + Z` 轴构造公式
|
||||
|
||||
### Task 9.1 通行空间与物体贴合语义统一
|
||||
|
||||
涉及文件:
|
||||
|
||||
- [AnimationControlViewModel.cs](/C:/Users/Tellme/apps/NavisworksTransport-rail-mount-modes/src/UI/WPF/ViewModels/AnimationControlViewModel.cs)
|
||||
- [PathAnimationManager.cs](/C:/Users/Tellme/apps/NavisworksTransport-rail-mount-modes/src/Core/Animation/PathAnimationManager.cs)
|
||||
|
||||
目标:
|
||||
|
||||
- 通行空间显示、起点贴合、动画帧贴合使用统一尺寸语义
|
||||
- Rail 轨上/轨下模式下,贴合面与留缝面保持一致
|
||||
|
||||
经验约束:
|
||||
|
||||
- `轨下安装`:顶面贴路径,底面留单侧间隙
|
||||
- `轨上安装`:底面贴路径,顶面留单侧间隙
|
||||
- 真实物体尺寸必须在选择物体时固定下来,并传入动画管理器
|
||||
|
||||
验收:
|
||||
|
||||
- 起点贴合正确后,动画第一帧不再出现固定间隙
|
||||
- 通行空间与真实物体的贴合面语义一致
|
||||
|
||||
## 5. 已知问题与注意事项
|
||||
|
||||
### 5.1 当前不应优先展开的模块
|
||||
@ -333,6 +363,32 @@
|
||||
|
||||
必须让错误显式暴露。
|
||||
|
||||
### 5.3 不能使用不完整构建产物部署 WPF 插件
|
||||
|
||||
实施过程中必须区分:
|
||||
|
||||
- 单元测试所需的程序集产物
|
||||
- 可用于 Navisworks 运行的完整 WPF 插件产物
|
||||
|
||||
对于本项目:
|
||||
|
||||
- 仅有 `TransportPlugin.dll` 被复制成功,不代表插件可用
|
||||
- 还必须确保 `TransportPlugin.g.resources` 中包含完整的主视图 `.baml`
|
||||
|
||||
最低要求:
|
||||
|
||||
- 主项目构建成功后再部署
|
||||
- 如遇异常,应检查关键资源是否存在:
|
||||
- `LogisticsControlPanel.baml`
|
||||
- `PathEditingView.baml`
|
||||
- `AnimationControlView.baml`
|
||||
- `LayerManagementView.baml`
|
||||
|
||||
否则可能出现:
|
||||
|
||||
- 插件布局恢复失败
|
||||
- 手工打开插件窗口即崩溃
|
||||
|
||||
## 6. 当前实施优先顺序
|
||||
|
||||
建议实际推进顺序如下:
|
||||
|
||||
@ -1,117 +0,0 @@
|
||||
{
|
||||
"PathPlanningData": {
|
||||
"version": "1.0",
|
||||
"generator": "NavisworksTransport",
|
||||
"timestamp": "2025-11-07T15:30:00",
|
||||
"ProjectInfo": {
|
||||
"name": "测试物流路径",
|
||||
"description": "用于测试JSON导入导出功能",
|
||||
"units": "meters",
|
||||
"coordinateSystem": "Global"
|
||||
},
|
||||
"Routes": [
|
||||
{
|
||||
"id": "route001",
|
||||
"name": "测试路径1",
|
||||
"description": "从入口到仓库的路径",
|
||||
"totalLength": 45.6,
|
||||
"objectLimits": {
|
||||
"maxLength": 2.0,
|
||||
"maxWidth": 1.5,
|
||||
"maxHeight": 2.0,
|
||||
"safetyMargin": 0.25
|
||||
},
|
||||
"gridSize": 0.5,
|
||||
"created": "2025-11-07T10:00:00",
|
||||
"points": [
|
||||
{
|
||||
"id": "point001",
|
||||
"name": "起点",
|
||||
"type": "StartPoint",
|
||||
"index": 0,
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"created": "2025-11-07T10:00:00"
|
||||
},
|
||||
{
|
||||
"id": "point002",
|
||||
"name": "路径点1",
|
||||
"type": "WayPoint",
|
||||
"index": 1,
|
||||
"x": 10.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0,
|
||||
"created": "2025-11-07T10:00:01"
|
||||
},
|
||||
{
|
||||
"id": "point003",
|
||||
"name": "路径点2",
|
||||
"type": "WayPoint",
|
||||
"index": 2,
|
||||
"x": 20.0,
|
||||
"y": 5.0,
|
||||
"z": 0.0,
|
||||
"created": "2025-11-07T10:00:02"
|
||||
},
|
||||
{
|
||||
"id": "point004",
|
||||
"name": "终点",
|
||||
"type": "EndPoint",
|
||||
"index": 3,
|
||||
"x": 30.0,
|
||||
"y": 10.0,
|
||||
"z": 0.0,
|
||||
"created": "2025-11-07T10:00:03"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "route002",
|
||||
"name": "测试路径2",
|
||||
"description": "从仓库到出口的路径",
|
||||
"totalLength": 32.4,
|
||||
"objectLimits": {
|
||||
"maxLength": 2.0,
|
||||
"maxWidth": 1.5,
|
||||
"maxHeight": 2.0,
|
||||
"safetyMargin": 0.25
|
||||
},
|
||||
"gridSize": 0.5,
|
||||
"created": "2025-11-07T10:05:00",
|
||||
"points": [
|
||||
{
|
||||
"id": "point005",
|
||||
"name": "起点",
|
||||
"type": "StartPoint",
|
||||
"index": 0,
|
||||
"x": 30.0,
|
||||
"y": 10.0,
|
||||
"z": 0.0,
|
||||
"created": "2025-11-07T10:05:00"
|
||||
},
|
||||
{
|
||||
"id": "point006",
|
||||
"name": "路径点1",
|
||||
"type": "WayPoint",
|
||||
"index": 1,
|
||||
"x": 25.0,
|
||||
"y": 8.0,
|
||||
"z": 0.0,
|
||||
"created": "2025-11-07T10:05:01"
|
||||
},
|
||||
{
|
||||
"id": "point007",
|
||||
"name": "终点",
|
||||
"type": "EndPoint",
|
||||
"index": 2,
|
||||
"x": 20.0,
|
||||
"y": 5.0,
|
||||
"z": 0.0,
|
||||
"created": "2025-11-07T10:05:02"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@ using Autodesk.Navisworks.Api.Clash;
|
||||
using NavisworksTransport.Core;
|
||||
using NavisworksTransport.Core.Animation;
|
||||
using NavisworksTransport.Utils;
|
||||
using NavisworksTransport.Utils.CoordinateSystem;
|
||||
using NavisworksTransport.UI.WPF.Views;
|
||||
|
||||
namespace NavisworksTransport.Commands
|
||||
@ -367,11 +368,11 @@ namespace NavisworksTransport.Commands
|
||||
var virtualObject = VirtualObjectManager.Instance.CurrentVirtualObject;
|
||||
if (virtualObject != null)
|
||||
{
|
||||
var bounds = virtualObject.BoundingBox();
|
||||
var pam = PathAnimationManager.GetInstance();
|
||||
var position = GetAnimatedObjectReportedPosition(virtualObject, pam);
|
||||
LogManager.Info(string.Format("[默认截图前] 虚拟物体位置: ({0:F2},{1:F2},{2:F2}), PAM记录朝向: {3:F2}°, customRotation={4}",
|
||||
bounds.Center.X, bounds.Center.Y, bounds.Min.Z,
|
||||
pam.CurrentYaw * 180 / Math.PI, pam.HasCurrentRotation));
|
||||
position.X, position.Y, position.Z,
|
||||
pam.CurrentYaw * 180 / Math.PI, pam.HasTrackedRotation));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -397,11 +398,11 @@ namespace NavisworksTransport.Commands
|
||||
virtualObject = VirtualObjectManager.Instance.CurrentVirtualObject;
|
||||
if (virtualObject != null)
|
||||
{
|
||||
var bounds = virtualObject.BoundingBox();
|
||||
var pam = PathAnimationManager.GetInstance();
|
||||
var position = GetAnimatedObjectReportedPosition(virtualObject, pam);
|
||||
LogManager.Info(string.Format("[默认截图后] 虚拟物体位置: ({0:F2},{1:F2},{2:F2}), PAM记录朝向: {3:F2}°, customRotation={4}",
|
||||
bounds.Center.X, bounds.Center.Y, bounds.Min.Z,
|
||||
pam.CurrentYaw * 180 / Math.PI, pam.HasCurrentRotation));
|
||||
position.X, position.Y, position.Z,
|
||||
pam.CurrentYaw * 180 / Math.PI, pam.HasTrackedRotation));
|
||||
}
|
||||
|
||||
if (screenshotPath != null)
|
||||
@ -609,6 +610,23 @@ namespace NavisworksTransport.Commands
|
||||
return PathPlanningResult<CollisionReportResult>.Success(result, message);
|
||||
}
|
||||
|
||||
private static Point3D GetAnimatedObjectReportedPosition(ModelItem animatedObject, PathAnimationManager pam)
|
||||
{
|
||||
if (animatedObject == null)
|
||||
{
|
||||
return new Point3D(0, 0, 0);
|
||||
}
|
||||
|
||||
if (pam != null && pam.ControlsAnimatedObject(animatedObject))
|
||||
{
|
||||
return pam.GetObjectCurrentPosition(animatedObject).Position;
|
||||
}
|
||||
|
||||
var bounds = animatedObject.BoundingBox();
|
||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
return ModelItemTransformHelper.GetHostBottomAnchorPoint(bounds, adapter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -96,6 +96,9 @@ namespace NavisworksTransport.Core.Animation
|
||||
private double _virtualObjectLength = 0; // 虚拟物体长度(模型单位)
|
||||
private double _virtualObjectWidth = 0; // 虚拟物体宽度(模型单位)
|
||||
private double _virtualObjectHeight = 0; // 虚拟物体高度(模型单位)
|
||||
private double _realObjectLength = 0; // 真实物体长度(模型单位,固定物理尺寸)
|
||||
private double _realObjectWidth = 0; // 真实物体宽度(模型单位,固定物理尺寸)
|
||||
private double _realObjectHeight = 0; // 真实物体高度(模型单位,固定物理尺寸)
|
||||
private List<Point3D> _pathPoints;
|
||||
private List<ModelItem> _manualCollisionTargets = new List<ModelItem>();
|
||||
private bool _manualCollisionOverrideEnabled = false;
|
||||
@ -157,12 +160,12 @@ namespace NavisworksTransport.Core.Animation
|
||||
|
||||
private Transform3D _originalTransform;
|
||||
private Point3D _originalCenter; // 存储部件的原始中心位置
|
||||
private Point3D _currentPosition; // 存储部件的当前位置
|
||||
private Point3D _trackedPosition; // 动画管理器内部跟踪的位置状态,不等于宿主即时读回位置
|
||||
private AnimationState _currentState = AnimationState.Idle;
|
||||
private double _pausedProgress = 0.0; // 暂停时的进度(0-1之间)
|
||||
private double _currentYaw = 0.0; // 当前偏航角(弧度)
|
||||
private Rotation3D _currentRotation = Rotation3D.Identity; // 当前完整姿态
|
||||
private bool _hasCurrentRotation = false; // 当前是否使用完整姿态
|
||||
private Rotation3D _trackedRotation = Rotation3D.Identity; // 动画管理器内部跟踪的完整姿态
|
||||
private bool _hasTrackedRotation = false; // 当前是否使用完整姿态
|
||||
|
||||
// TimeLiner 集成
|
||||
private TimeLinerIntegrationManager _timeLinerManager;
|
||||
@ -422,11 +425,11 @@ namespace NavisworksTransport.Core.Animation
|
||||
// 2. 重置内部跟踪变量(同步到原始状态)
|
||||
// 注意:ResetPermanentTransform 后物体的 Transform 属性会自动变回原始值
|
||||
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(objectToRestore.Transform);
|
||||
_currentRotation = objectToRestore.Transform.Factor().Rotation;
|
||||
_hasCurrentRotation = true;
|
||||
_trackedRotation = objectToRestore.Transform.Factor().Rotation;
|
||||
_hasTrackedRotation = true;
|
||||
|
||||
var originalBoundingBox = objectToRestore.BoundingBox();
|
||||
_currentPosition = GetTrackedObjectPosition(objectToRestore, _route?.PathType == PathType.Rail && !isVirtual);
|
||||
_trackedPosition = GetTrackedObjectPosition(objectToRestore, _route?.PathType == PathType.Rail && !isVirtual);
|
||||
|
||||
string objectName = isVirtual ? "虚拟物体" : objectToRestore.DisplayName;
|
||||
LogManager.Info($"[归位] {objectName} 已彻底恢复到原始位置, yaw={_currentYaw:F3}");
|
||||
@ -469,6 +472,18 @@ namespace NavisworksTransport.Core.Animation
|
||||
_virtualObjectHeight = height; // 模型单位
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置真实物体的固定物理尺寸(模型单位)。
|
||||
/// 一旦动画开始生成,后续 Rail 帧和起点贴合应使用同一份尺寸语义,
|
||||
/// 不能再从已经旋转后的当前 AABB 重新推导高度。
|
||||
/// </summary>
|
||||
public void SetRealObjectDimensions(double length, double width, double height)
|
||||
{
|
||||
_realObjectLength = length;
|
||||
_realObjectWidth = width;
|
||||
_realObjectHeight = height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置动画参数(批处理专用)
|
||||
/// </summary>
|
||||
@ -589,12 +604,12 @@ namespace NavisworksTransport.Core.Animation
|
||||
_animatedObject = animatedObject;
|
||||
_originalTransform = animatedObject.Transform;
|
||||
_originalCenter = animatedObject.BoundingBox().Center;
|
||||
_currentPosition = GetTrackedObjectPosition(animatedObject, _route?.PathType == PathType.Rail && !_isVirtualObject);
|
||||
_trackedPosition = GetTrackedObjectPosition(animatedObject, _route?.PathType == PathType.Rail && !_isVirtualObject);
|
||||
|
||||
// 统一逻辑:从当前 Transform 中提取实际朝向(无论虚拟物体还是普通物体)
|
||||
_currentYaw = ModelItemTransformHelper.GetYawFromTransform(_originalTransform);
|
||||
_currentRotation = _originalTransform.Factor().Rotation;
|
||||
_hasCurrentRotation = true;
|
||||
_trackedRotation = _originalTransform.Factor().Rotation;
|
||||
_hasTrackedRotation = true;
|
||||
}
|
||||
|
||||
if (pathPoints != null)
|
||||
@ -835,12 +850,18 @@ namespace NavisworksTransport.Core.Animation
|
||||
}
|
||||
|
||||
// 3. 生成每一帧
|
||||
double distancePerFrameInModelUnits = totalLengthInModelUnits / totalFrames;
|
||||
double distancePerFrameInModelUnits = totalFrames > 1
|
||||
? totalLengthInModelUnits / (totalFrames - 1)
|
||||
: 0.0;
|
||||
LogManager.Info($"总帧数: {totalFrames}, 总长度: {totalLengthInModelUnits / metersToModelUnits:F2}米, 每帧移动距离: {distancePerFrameInModelUnits / metersToModelUnits:F4}米");
|
||||
|
||||
for (int i = 0; i < totalFrames; i++)
|
||||
{
|
||||
double targetDistance = i * distancePerFrameInModelUnits; // 按距离采样(模型单位),不是按进度
|
||||
double targetDistance = i * distancePerFrameInModelUnits; // 按距离采样(模型单位),最后一帧应精确到终点
|
||||
if (targetDistance > totalLengthInModelUnits)
|
||||
{
|
||||
targetDistance = totalLengthInModelUnits;
|
||||
}
|
||||
|
||||
Point3D framePosition;
|
||||
Point3D previousFramePoint = Point3D.Origin;
|
||||
@ -1091,10 +1112,10 @@ namespace NavisworksTransport.Core.Animation
|
||||
(virtualBoundingBox.Min.Y + virtualBoundingBox.Max.Y + colliderBox.Min.Y + colliderBox.Max.Y) / 4,
|
||||
(virtualBoundingBox.Min.Z + virtualBoundingBox.Max.Z + colliderBox.Min.Z + colliderBox.Max.Z) / 4
|
||||
),
|
||||
// 碰撞结果中的 Item1Position 必须与后续恢复链路的基准点语义一致:
|
||||
// 碰撞结果中的 AnimatedObjectTrackedPosition 必须与后续恢复链路的动画跟踪点语义一致:
|
||||
// 1. 二维 yaw 路径沿用成熟旧逻辑,记录包围盒中心;
|
||||
// 2. 三维 customRotation 路径记录动画跟踪点(framePosition)。
|
||||
Item1Position = frame.HasCustomRotation
|
||||
AnimatedObjectTrackedPosition = frame.HasCustomRotation
|
||||
? new Point3D(
|
||||
framePosition.X,
|
||||
framePosition.Y,
|
||||
@ -1104,9 +1125,9 @@ namespace NavisworksTransport.Core.Animation
|
||||
framePosition.Y,
|
||||
framePosition.Z + (virtualBoundingBox.Max.Z - virtualBoundingBox.Min.Z) / 2),
|
||||
Item2Position = GetObjectPosition(collider),
|
||||
Item1YawRadians = actualYawRadians, // 记录运动物体实际播放朝向
|
||||
Item1Rotation = frame.HasCustomRotation ? frame.Rotation : Rotation3D.Identity,
|
||||
Item1HasCustomRotation = frame.HasCustomRotation,
|
||||
AnimatedObjectTrackedYawRadians = actualYawRadians, // 记录动画跟踪点对应的运动物体实际播放朝向
|
||||
AnimatedObjectTrackedRotation = frame.HasCustomRotation ? frame.Rotation : Rotation3D.Identity,
|
||||
AnimatedObjectHasTrackedRotation = frame.HasCustomRotation,
|
||||
HasPositionInfo = true
|
||||
};
|
||||
|
||||
@ -1116,7 +1137,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
$"原始Yaw={yawRadians * 180.0 / Math.PI:F2}°, " +
|
||||
$"实际Yaw={actualYawRadians * 180.0 / Math.PI:F2}°, " +
|
||||
$"customRotation={frame.HasCustomRotation}, " +
|
||||
$"保存位置=({collisionResult.Item1Position.X:F3}, {collisionResult.Item1Position.Y:F3}, {collisionResult.Item1Position.Z:F3})");
|
||||
$"保存位置=({collisionResult.AnimatedObjectTrackedPosition.X:F3}, {collisionResult.AnimatedObjectTrackedPosition.Y:F3}, {collisionResult.AnimatedObjectTrackedPosition.Z:F3})");
|
||||
|
||||
frame.Collisions.Add(collisionResult);
|
||||
_allCollisionResults.Add(collisionResult);
|
||||
@ -1130,7 +1151,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
var framesWithCollision = _animationFrames.Count(f => f.HasCollision);
|
||||
var totalCollisions = _animationFrames.Sum(f => f.Collisions.Count);
|
||||
// 检查碰撞结果是否包含位置信息
|
||||
var collisionsWithPosition = _allCollisionResults.Count(c => c.HasPositionInfo && c.Item1Position != null);
|
||||
var collisionsWithPosition = _allCollisionResults.Count(c => c.HasPositionInfo && c.AnimatedObjectTrackedPosition != null);
|
||||
|
||||
LogManager.Info($"=== 预计算完成 ===");
|
||||
LogManager.Info($"总帧数: {_animationFrames.Count}");
|
||||
@ -1363,12 +1384,12 @@ namespace NavisworksTransport.Core.Animation
|
||||
|
||||
if (firstFrame.HasCustomRotation && _route?.PathType == PathType.Rail)
|
||||
{
|
||||
var dx = _currentPosition.X - firstFrame.Position.X;
|
||||
var dy = _currentPosition.Y - firstFrame.Position.Y;
|
||||
var dz = _currentPosition.Z - firstFrame.Position.Z;
|
||||
var dx = _trackedPosition.X - firstFrame.Position.X;
|
||||
var dy = _trackedPosition.Y - firstFrame.Position.Y;
|
||||
var dz = _trackedPosition.Z - firstFrame.Position.Z;
|
||||
distanceToStart = Math.Sqrt(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
var currentLinear = new Transform3D(_currentRotation).Linear;
|
||||
var currentLinear = new Transform3D(_trackedRotation).Linear;
|
||||
var targetLinear = new Transform3D(firstFrame.Rotation).Linear;
|
||||
double rotationDiff =
|
||||
Math.Abs(currentLinear.Get(0, 0) - targetLinear.Get(0, 0)) +
|
||||
@ -1701,6 +1722,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
|
||||
// 动画自然结束时保持物体在最终位置(不移回起点)
|
||||
LogManager.Info("动画播放自然结束,物体保持在最终位置");
|
||||
LogRailEndAlignmentDiagnostics();
|
||||
|
||||
// 直接设置为完成状态,避免中间状态切换
|
||||
SetState(AnimationState.Finished);
|
||||
@ -1774,6 +1796,85 @@ namespace NavisworksTransport.Core.Animation
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出 Rail 路径动画终点对齐诊断。
|
||||
/// 只做日志,不改变业务行为,用于确认:
|
||||
/// 1. 路径终点锚点
|
||||
/// 2. 最后一帧动画跟踪点
|
||||
/// 3. 动画结束时的实际跟踪点
|
||||
/// 4. 沿路径前进方向的终点偏差
|
||||
/// </summary>
|
||||
private void LogRailEndAlignmentDiagnostics()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_route == null || _route.PathType != PathType.Rail || _pathPoints == null || _pathPoints.Count < 2 || _animationFrames == null || _animationFrames.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Point3D terminalAnchorPoint = _pathPoints[_pathPoints.Count - 1];
|
||||
Point3D previousAnchorPoint = _pathPoints[_pathPoints.Count - 2];
|
||||
var lastFrame = _animationFrames[_animationFrames.Count - 1];
|
||||
Point3D finalTrackedPoint = _trackedPosition;
|
||||
|
||||
double objectHeight = GetAnimatedObjectHeight();
|
||||
Point3D expectedTrackedEndPoint = RailPathPoseHelper.ResolveBottomPosition(
|
||||
_route,
|
||||
terminalAnchorPoint,
|
||||
previousAnchorPoint,
|
||||
terminalAnchorPoint,
|
||||
objectHeight);
|
||||
|
||||
Vector3D forward = new Vector3D(
|
||||
terminalAnchorPoint.X - previousAnchorPoint.X,
|
||||
terminalAnchorPoint.Y - previousAnchorPoint.Y,
|
||||
terminalAnchorPoint.Z - previousAnchorPoint.Z);
|
||||
|
||||
double forwardLength = Math.Sqrt(forward.X * forward.X + forward.Y * forward.Y + forward.Z * forward.Z);
|
||||
if (forwardLength < 1e-9)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3D normalizedForward = new Vector3D(
|
||||
forward.X / forwardLength,
|
||||
forward.Y / forwardLength,
|
||||
forward.Z / forwardLength);
|
||||
|
||||
Vector3D frameDelta = new Vector3D(
|
||||
lastFrame.Position.X - expectedTrackedEndPoint.X,
|
||||
lastFrame.Position.Y - expectedTrackedEndPoint.Y,
|
||||
lastFrame.Position.Z - expectedTrackedEndPoint.Z);
|
||||
Vector3D finalDelta = new Vector3D(
|
||||
finalTrackedPoint.X - expectedTrackedEndPoint.X,
|
||||
finalTrackedPoint.Y - expectedTrackedEndPoint.Y,
|
||||
finalTrackedPoint.Z - expectedTrackedEndPoint.Z);
|
||||
|
||||
double frameForwardDelta = Dot(frameDelta, normalizedForward);
|
||||
double finalForwardDelta = Dot(finalDelta, normalizedForward);
|
||||
|
||||
LogManager.Info(
|
||||
$"[Rail终点诊断] 终点锚点=({terminalAnchorPoint.X:F3},{terminalAnchorPoint.Y:F3},{terminalAnchorPoint.Z:F3}), " +
|
||||
$"期望跟踪点=({expectedTrackedEndPoint.X:F3},{expectedTrackedEndPoint.Y:F3},{expectedTrackedEndPoint.Z:F3}), " +
|
||||
$"最后一帧跟踪点=({lastFrame.Position.X:F3},{lastFrame.Position.Y:F3},{lastFrame.Position.Z:F3}), " +
|
||||
$"动画结束跟踪点=({finalTrackedPoint.X:F3},{finalTrackedPoint.Y:F3},{finalTrackedPoint.Z:F3})");
|
||||
LogManager.Info(
|
||||
$"[Rail终点诊断] 前进方向=({normalizedForward.X:F4},{normalizedForward.Y:F4},{normalizedForward.Z:F4}), " +
|
||||
$"最后一帧偏差=({frameDelta.X:F3},{frameDelta.Y:F3},{frameDelta.Z:F3}), 沿前进轴={frameForwardDelta:F3}, " +
|
||||
$"结束偏差=({finalDelta.X:F3},{finalDelta.Y:F3},{finalDelta.Z:F3}), 沿前进轴={finalForwardDelta:F3}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[Rail终点诊断] 输出失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static double Dot(Vector3D a, Vector3D b)
|
||||
{
|
||||
return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 暂停动画
|
||||
/// </summary>
|
||||
@ -2147,9 +2248,9 @@ namespace NavisworksTransport.Core.Animation
|
||||
|
||||
// 计算平移和旋转的增量
|
||||
var deltaPos = new Vector3D(
|
||||
newPosition.X - _currentPosition.X,
|
||||
newPosition.Y - _currentPosition.Y,
|
||||
newPosition.Z - _currentPosition.Z
|
||||
newPosition.X - _trackedPosition.X,
|
||||
newPosition.Y - _trackedPosition.Y,
|
||||
newPosition.Z - _trackedPosition.Z
|
||||
);
|
||||
|
||||
Transform3D incrementalTransform;
|
||||
@ -2164,18 +2265,18 @@ namespace NavisworksTransport.Core.Animation
|
||||
//LogManager.Debug($"[UpdateObjectPosition] 当前yaw={_currentYaw * 180 / Math.PI:F2}度, 目标yaw={newYaw * 180 / Math.PI:F2}度, 旋转增量deltaYaw={deltaYaw * 180 / Math.PI:F2}度");
|
||||
|
||||
// 计算绕当前位置旋转的等效变换:
|
||||
// 1. 如果绕原点旋转deltaYaw,当前位置_currentPosition会移动到哪里?
|
||||
// 1. 如果绕原点旋转deltaYaw,当前位置_trackedPosition会移动到哪里?
|
||||
double cos = Math.Cos(deltaYaw);
|
||||
double sin = Math.Sin(deltaYaw);
|
||||
double rotatedX = _currentPosition.X * cos - _currentPosition.Y * sin;
|
||||
double rotatedY = _currentPosition.X * sin + _currentPosition.Y * cos;
|
||||
double rotatedX = _trackedPosition.X * cos - _trackedPosition.Y * sin;
|
||||
double rotatedY = _trackedPosition.X * sin + _trackedPosition.Y * cos;
|
||||
|
||||
// 2. 但我们希望物体绕自己旋转,位置移动到newPosition
|
||||
// 所以需要的平移 = newPosition - (旋转后的位置)
|
||||
var compensatedTranslation = new Vector3D(
|
||||
newPosition.X - rotatedX,
|
||||
newPosition.Y - rotatedY,
|
||||
newPosition.Z - _currentPosition.Z // Z保持deltaPos
|
||||
newPosition.Z - _trackedPosition.Z // Z保持deltaPos
|
||||
);
|
||||
|
||||
// 3. 组合:先旋转(绕原点),再平移(补偿+目标位置)
|
||||
@ -2187,8 +2288,8 @@ namespace NavisworksTransport.Core.Animation
|
||||
incrementalTransform = components.Combine();
|
||||
|
||||
_currentYaw = newYaw;
|
||||
_currentRotation = Rotation3D.Identity;
|
||||
_hasCurrentRotation = false;
|
||||
_trackedRotation = Rotation3D.Identity;
|
||||
_hasTrackedRotation = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2200,8 +2301,8 @@ namespace NavisworksTransport.Core.Animation
|
||||
// 应用增量变换(false = 增量模式)
|
||||
doc.Models.OverridePermanentTransform(modelItems, incrementalTransform, false);
|
||||
|
||||
// 更新当前位置
|
||||
_currentPosition = newPosition;
|
||||
// 更新跟踪位置
|
||||
_trackedPosition = newPosition;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -2223,16 +2324,28 @@ namespace NavisworksTransport.Core.Animation
|
||||
}
|
||||
else if (_animatedObject != null)
|
||||
{
|
||||
var currentRotation = _hasCurrentRotation
|
||||
? _currentRotation
|
||||
Point3D currentPositionForTransform = _trackedPosition;
|
||||
var currentRotation = _hasTrackedRotation
|
||||
? _trackedRotation
|
||||
: _animatedObject.Transform.Factor().Rotation;
|
||||
try
|
||||
{
|
||||
var actualHostPosition = GetTrackedObjectPosition(_animatedObject, _route?.PathType == PathType.Rail && !_isVirtualObject);
|
||||
currentPositionForTransform = actualHostPosition;
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {_animatedObject.DisplayName} 宿主即时读回点=({actualHostPosition.X:F3},{actualHostPosition.Y:F3},{actualHostPosition.Z:F3})");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[动画姿态入口] 读取宿主实际状态失败: {ex.Message}");
|
||||
}
|
||||
var currentLinear = new Transform3D(currentRotation).Linear;
|
||||
var targetLinear = new Transform3D(newRotation).Linear;
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {_animatedObject.DisplayName} 当前点=({_currentPosition.X:F3},{_currentPosition.Y:F3},{_currentPosition.Z:F3}), " +
|
||||
$"[动画姿态入口] {_animatedObject.DisplayName} 跟踪点=({_trackedPosition.X:F3},{_trackedPosition.Y:F3},{_trackedPosition.Z:F3}), " +
|
||||
$"目标点=({newPosition.X:F3},{newPosition.Y:F3},{newPosition.Z:F3})");
|
||||
LogManager.Info(
|
||||
$"[动画姿态入口] {_animatedObject.DisplayName} 当前姿态: " +
|
||||
$"[动画姿态入口] {_animatedObject.DisplayName} 跟踪姿态: " +
|
||||
$"X=({currentLinear.Get(0, 0):F4},{currentLinear.Get(1, 0):F4},{currentLinear.Get(2, 0):F4}), " +
|
||||
$"Y=({currentLinear.Get(0, 1):F4},{currentLinear.Get(1, 1):F4},{currentLinear.Get(2, 1):F4}), " +
|
||||
$"Z=({currentLinear.Get(0, 2):F4},{currentLinear.Get(1, 2):F4},{currentLinear.Get(2, 2):F4})");
|
||||
@ -2243,7 +2356,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
$"Z=({targetLinear.Get(0, 2):F4},{targetLinear.Get(1, 2):F4},{targetLinear.Get(2, 2):F4})");
|
||||
ModelItemTransformHelper.MoveItemIncrementallyToPositionAndRotation(
|
||||
_animatedObject,
|
||||
_currentPosition,
|
||||
currentPositionForTransform,
|
||||
currentRotation,
|
||||
newPosition,
|
||||
newRotation);
|
||||
@ -2253,9 +2366,9 @@ namespace NavisworksTransport.Core.Animation
|
||||
return;
|
||||
}
|
||||
|
||||
_currentPosition = newPosition;
|
||||
_currentRotation = newRotation;
|
||||
_hasCurrentRotation = true;
|
||||
_trackedPosition = newPosition;
|
||||
_trackedRotation = newRotation;
|
||||
_hasTrackedRotation = true;
|
||||
_currentYaw = ModelItemTransformHelper.GetYawFromRotation(newRotation);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -2353,7 +2466,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
return (null, 0);
|
||||
}
|
||||
|
||||
var position = _currentPosition;
|
||||
var position = _trackedPosition;
|
||||
// 🔥 关键:使用 _currentYaw(实际当前朝向),而不是 Transform 的 CAD 原始朝向
|
||||
// Transform 返回的是 CAD 设计时的原始朝向,不是动画后的实际朝向
|
||||
return (position, _currentYaw);
|
||||
@ -2375,8 +2488,8 @@ namespace NavisworksTransport.Core.Animation
|
||||
var (position, yaw) = GetObjectCurrentPosition(obj);
|
||||
_savedObjectPosition = position;
|
||||
_savedObjectYaw = yaw;
|
||||
_savedObjectRotation = _currentRotation;
|
||||
_savedObjectHasCustomRotation = _hasCurrentRotation;
|
||||
_savedObjectRotation = _trackedRotation;
|
||||
_savedObjectHasCustomRotation = _hasTrackedRotation;
|
||||
_hasSavedObjectState = true;
|
||||
LogManager.Info($"已保存物体状态: pos=({position.X:F2},{position.Y:F2},{position.Z:F2}), yaw={yaw * 180 / Math.PI:F2}度, customRotation={_savedObjectHasCustomRotation}");
|
||||
}
|
||||
@ -2409,10 +2522,10 @@ namespace NavisworksTransport.Core.Animation
|
||||
var originalAnimatedObject = _animatedObject;
|
||||
_animatedObject = obj;
|
||||
|
||||
_currentPosition = _savedObjectPosition;
|
||||
_trackedPosition = _savedObjectPosition;
|
||||
_currentYaw = _savedObjectYaw;
|
||||
_currentRotation = _savedObjectRotation;
|
||||
_hasCurrentRotation = _savedObjectHasCustomRotation;
|
||||
_trackedRotation = _savedObjectRotation;
|
||||
_hasTrackedRotation = _savedObjectHasCustomRotation;
|
||||
|
||||
if (_savedObjectHasCustomRotation)
|
||||
{
|
||||
@ -2753,7 +2866,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
// 清空对象引用
|
||||
_animatedObject = null;
|
||||
_pathPoints?.Clear();
|
||||
_currentPosition = new Point3D(0, 0, 0);
|
||||
_trackedPosition = new Point3D(0, 0, 0);
|
||||
_originalCenter = new Point3D(0, 0, 0);
|
||||
|
||||
LogManager.Info("[PathAnimationManager] 对象引用已清理");
|
||||
@ -2955,8 +3068,8 @@ namespace NavisworksTransport.Core.Animation
|
||||
/// 用于保存物体当前状态(因为Transform返回的是CAD原始值)
|
||||
/// </summary>
|
||||
public double CurrentYaw => _currentYaw;
|
||||
public Rotation3D CurrentRotation => _currentRotation;
|
||||
public bool HasCurrentRotation => _hasCurrentRotation;
|
||||
public Rotation3D TrackedRotation => _trackedRotation;
|
||||
public bool HasTrackedRotation => _hasTrackedRotation;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前动画物体高度(模型单位)
|
||||
@ -2968,6 +3081,11 @@ namespace NavisworksTransport.Core.Animation
|
||||
return _virtualObjectHeight;
|
||||
}
|
||||
|
||||
if (_realObjectHeight > 0.0)
|
||||
{
|
||||
return _realObjectHeight;
|
||||
}
|
||||
|
||||
if (_animatedObject == null)
|
||||
{
|
||||
throw new InvalidOperationException("动画对象为空,无法获取物体高度");
|
||||
@ -3062,7 +3180,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
{
|
||||
_originalTransform = animatedObject.Transform;
|
||||
_originalCenter = animatedObject.BoundingBox().Center;
|
||||
_currentPosition = GetTrackedObjectPosition(animatedObject, _route?.PathType == PathType.Rail && !_isVirtualObject);
|
||||
_trackedPosition = GetTrackedObjectPosition(animatedObject, _route?.PathType == PathType.Rail && !_isVirtualObject);
|
||||
|
||||
// 保持当前的 _currentYaw(因为物体可能已经被 MoveObjectToPathStart 旋转)
|
||||
// 不要从 Transform 中提取,因为 Transform 返回的是原始值,不是当前值
|
||||
@ -3087,13 +3205,8 @@ namespace NavisworksTransport.Core.Animation
|
||||
}
|
||||
|
||||
var bounds = item.BoundingBox();
|
||||
if (useStableRailAnchor)
|
||||
{
|
||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
return ModelItemTransformHelper.GetHostBottomAnchorPoint(bounds, adapter);
|
||||
}
|
||||
|
||||
return new Point3D(bounds.Center.X, bounds.Center.Y, bounds.Min.Z);
|
||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
return ModelItemTransformHelper.GetHostBottomAnchorPoint(bounds, adapter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -7,6 +7,7 @@ using Autodesk.Navisworks.Api.Clash;
|
||||
using NavisworksTransport.Core;
|
||||
using NavisworksTransport.Core.Animation;
|
||||
using NavisworksTransport.Utils;
|
||||
using NavisworksTransport.Utils.CoordinateSystem;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
@ -174,11 +175,11 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
private class CollisionPositionInfo
|
||||
{
|
||||
public Point3D Item1Position { get; set; }
|
||||
public Point3D AnimatedObjectTrackedPosition { get; set; }
|
||||
public Point3D Item2Position { get; set; }
|
||||
public double Item1YawRadians { get; set; }
|
||||
public Rotation3D Item1Rotation { get; set; }
|
||||
public bool Item1HasCustomRotation { get; set; }
|
||||
public double AnimatedObjectTrackedYawRadians { get; set; }
|
||||
public Rotation3D AnimatedObjectTrackedRotation { get; set; }
|
||||
public bool AnimatedObjectHasTrackedRotation { get; set; }
|
||||
public bool HasPositionInfo { get; set; }
|
||||
}
|
||||
|
||||
@ -271,20 +272,20 @@ namespace NavisworksTransport
|
||||
};
|
||||
|
||||
// 保存碰撞时运动物体的位置和朝向(如果可用)
|
||||
if (collision.HasPositionInfo && collision.Item1Position != null)
|
||||
if (collision.HasPositionInfo && collision.AnimatedObjectTrackedPosition != null)
|
||||
{
|
||||
collisionRecord.Item1PosX = collision.Item1Position.X;
|
||||
collisionRecord.Item1PosY = collision.Item1Position.Y;
|
||||
collisionRecord.Item1PosZ = collision.Item1Position.Z;
|
||||
collisionRecord.Item1YawRadians = collision.Item1YawRadians;
|
||||
collisionRecord.Item1RotA = collision.Item1Rotation?.A;
|
||||
collisionRecord.Item1RotB = collision.Item1Rotation?.B;
|
||||
collisionRecord.Item1RotC = collision.Item1Rotation?.C;
|
||||
collisionRecord.Item1RotD = collision.Item1Rotation?.D;
|
||||
collisionRecord.Item1HasCustomRotation = collision.Item1HasCustomRotation;
|
||||
collisionRecord.AnimatedObjectTrackedPosX = collision.AnimatedObjectTrackedPosition.X;
|
||||
collisionRecord.AnimatedObjectTrackedPosY = collision.AnimatedObjectTrackedPosition.Y;
|
||||
collisionRecord.AnimatedObjectTrackedPosZ = collision.AnimatedObjectTrackedPosition.Z;
|
||||
collisionRecord.AnimatedObjectTrackedYawRadians = collision.AnimatedObjectTrackedYawRadians;
|
||||
collisionRecord.AnimatedObjectTrackedRotA = collision.AnimatedObjectTrackedRotation?.A;
|
||||
collisionRecord.AnimatedObjectTrackedRotB = collision.AnimatedObjectTrackedRotation?.B;
|
||||
collisionRecord.AnimatedObjectTrackedRotC = collision.AnimatedObjectTrackedRotation?.C;
|
||||
collisionRecord.AnimatedObjectTrackedRotD = collision.AnimatedObjectTrackedRotation?.D;
|
||||
collisionRecord.AnimatedObjectHasTrackedRotation = collision.AnimatedObjectHasTrackedRotation;
|
||||
collisionRecord.HasPositionInfo = true;
|
||||
|
||||
LogManager.Debug($"[保存碰撞对象] 记录运动物体位置: ({collisionRecord.Item1PosX:F2}, {collisionRecord.Item1PosY:F2}, {collisionRecord.Item1PosZ:F2}), yaw={collisionRecord.Item1YawRadians:F2} rad, customRotation={collisionRecord.Item1HasCustomRotation}");
|
||||
|
||||
LogManager.Debug($"[保存碰撞对象] 记录运动物体位置: ({collisionRecord.AnimatedObjectTrackedPosX:F2}, {collisionRecord.AnimatedObjectTrackedPosY:F2}, {collisionRecord.AnimatedObjectTrackedPosZ:F2}), yaw={collisionRecord.AnimatedObjectTrackedYawRadians:F2} rad, customRotation={collisionRecord.AnimatedObjectHasTrackedRotation}");
|
||||
}
|
||||
|
||||
collisionObjects.Add(collisionRecord);
|
||||
@ -495,26 +496,26 @@ namespace NavisworksTransport
|
||||
};
|
||||
|
||||
// 恢复碰撞时运动物体的位置和朝向(如果可用)
|
||||
if (obj.HasPositionInfo && obj.Item1PosX.HasValue && obj.Item1PosY.HasValue && obj.Item1PosZ.HasValue)
|
||||
if (obj.HasPositionInfo && obj.AnimatedObjectTrackedPosX.HasValue && obj.AnimatedObjectTrackedPosY.HasValue && obj.AnimatedObjectTrackedPosZ.HasValue)
|
||||
{
|
||||
collisionResult.Item1Position = new Point3D(obj.Item1PosX.Value, obj.Item1PosY.Value, obj.Item1PosZ.Value);
|
||||
collisionResult.Item1YawRadians = obj.Item1YawRadians ?? 0.0;
|
||||
collisionResult.Item1HasCustomRotation = obj.Item1HasCustomRotation;
|
||||
if (obj.Item1HasCustomRotation &&
|
||||
obj.Item1RotA.HasValue &&
|
||||
obj.Item1RotB.HasValue &&
|
||||
obj.Item1RotC.HasValue &&
|
||||
obj.Item1RotD.HasValue)
|
||||
collisionResult.AnimatedObjectTrackedPosition = new Point3D(obj.AnimatedObjectTrackedPosX.Value, obj.AnimatedObjectTrackedPosY.Value, obj.AnimatedObjectTrackedPosZ.Value);
|
||||
collisionResult.AnimatedObjectTrackedYawRadians = obj.AnimatedObjectTrackedYawRadians ?? 0.0;
|
||||
collisionResult.AnimatedObjectHasTrackedRotation = obj.AnimatedObjectHasTrackedRotation;
|
||||
if (obj.AnimatedObjectHasTrackedRotation &&
|
||||
obj.AnimatedObjectTrackedRotA.HasValue &&
|
||||
obj.AnimatedObjectTrackedRotB.HasValue &&
|
||||
obj.AnimatedObjectTrackedRotC.HasValue &&
|
||||
obj.AnimatedObjectTrackedRotD.HasValue)
|
||||
{
|
||||
collisionResult.Item1Rotation = new Rotation3D(
|
||||
obj.Item1RotA.Value,
|
||||
obj.Item1RotB.Value,
|
||||
obj.Item1RotC.Value,
|
||||
obj.Item1RotD.Value);
|
||||
collisionResult.AnimatedObjectTrackedRotation = new Rotation3D(
|
||||
obj.AnimatedObjectTrackedRotA.Value,
|
||||
obj.AnimatedObjectTrackedRotB.Value,
|
||||
obj.AnimatedObjectTrackedRotC.Value,
|
||||
obj.AnimatedObjectTrackedRotD.Value);
|
||||
}
|
||||
collisionResult.HasPositionInfo = true;
|
||||
|
||||
LogManager.Debug($"[加载碰撞结果] 恢复运动物体位置: ({obj.Item1PosX:F2}, {obj.Item1PosY:F2}, {obj.Item1PosZ:F2}), yaw={obj.Item1YawRadians:F2} rad, customRotation={obj.Item1HasCustomRotation}");
|
||||
|
||||
LogManager.Debug($"[加载碰撞结果] 恢复运动物体位置: ({obj.AnimatedObjectTrackedPosX:F2}, {obj.AnimatedObjectTrackedPosY:F2}, {obj.AnimatedObjectTrackedPosZ:F2}), yaw={obj.AnimatedObjectTrackedYawRadians:F2} rad, customRotation={obj.AnimatedObjectHasTrackedRotation}");
|
||||
}
|
||||
|
||||
results.Add(collisionResult);
|
||||
@ -796,22 +797,27 @@ namespace NavisworksTransport
|
||||
{
|
||||
var testAnimatedObject = candidate.Item1;
|
||||
var modelItems = new ModelItemCollection { testAnimatedObject };
|
||||
var targetPosition = candidate.Item1Position;
|
||||
var targetYaw = candidate.Item1YawRadians;
|
||||
var targetRotation = candidate.Item1Rotation;
|
||||
var targetPosition = candidate.AnimatedObjectTrackedPosition;
|
||||
var targetYaw = candidate.AnimatedObjectTrackedYawRadians;
|
||||
var targetRotation = candidate.AnimatedObjectTrackedRotation;
|
||||
|
||||
// 🔥 关键:为了最高精度,先回到CAD原始位置,再移动到目标位置
|
||||
// 避免累积误差影响碰撞检测精度
|
||||
doc.Models.ResetPermanentTransform(modelItems);
|
||||
|
||||
if (candidate.Item1HasCustomRotation && targetRotation != null)
|
||||
if (candidate.AnimatedObjectHasTrackedRotation && targetRotation != null)
|
||||
{
|
||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
var originalBounds = testAnimatedObject.BoundingBox();
|
||||
var originalCenter = originalBounds.Center;
|
||||
var originalTrackedPosition = ModelItemTransformHelper.GetHostBottomAnchorPoint(originalBounds, adapter);
|
||||
var originalRotation = testAnimatedObject.Transform.Factor().Rotation;
|
||||
LogManager.Info(
|
||||
$"[ClashDetective验证] 三维候选恢复: " +
|
||||
$"当前跟踪点=({originalTrackedPosition.X:F3},{originalTrackedPosition.Y:F3},{originalTrackedPosition.Z:F3}), " +
|
||||
$"目标跟踪点=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3})");
|
||||
ModelItemTransformHelper.MoveItemIncrementallyToPositionAndRotation(
|
||||
testAnimatedObject,
|
||||
originalCenter,
|
||||
originalTrackedPosition,
|
||||
originalRotation,
|
||||
targetPosition,
|
||||
targetRotation);
|
||||
@ -921,14 +927,14 @@ namespace NavisworksTransport
|
||||
var confirmedPositionKey = GetCollisionObjectKey(candidate.Item1, candidate.Item2);
|
||||
_confirmedCollisionPositions[confirmedPositionKey] = new CollisionPositionInfo
|
||||
{
|
||||
Item1Position = candidate.Item1Position,
|
||||
AnimatedObjectTrackedPosition = candidate.AnimatedObjectTrackedPosition,
|
||||
Item2Position = candidate.Item2Position,
|
||||
Item1YawRadians = candidate.Item1YawRadians,
|
||||
Item1Rotation = candidate.Item1Rotation,
|
||||
Item1HasCustomRotation = candidate.Item1HasCustomRotation,
|
||||
AnimatedObjectTrackedYawRadians = candidate.AnimatedObjectTrackedYawRadians,
|
||||
AnimatedObjectTrackedRotation = candidate.AnimatedObjectTrackedRotation,
|
||||
AnimatedObjectHasTrackedRotation = candidate.AnimatedObjectHasTrackedRotation,
|
||||
HasPositionInfo = candidate.HasPositionInfo
|
||||
};
|
||||
LogManager.Debug($"[ClashDetective确认] 记录碰撞位置: {confirmedPositionKey}, 位置: ({candidate.Item1Position.X:F2}, {candidate.Item1Position.Y:F2}, {candidate.Item1Position.Z:F2})");
|
||||
LogManager.Debug($"[ClashDetective确认] 记录碰撞位置: {confirmedPositionKey}, 位置: ({candidate.AnimatedObjectTrackedPosition.X:F2}, {candidate.AnimatedObjectTrackedPosition.Y:F2}, {candidate.AnimatedObjectTrackedPosition.Z:F2})");
|
||||
|
||||
int subResultIndex = 1;
|
||||
// 🔥 优化:预获取运动物体名称,避免在循环中重复获取
|
||||
@ -1037,11 +1043,11 @@ namespace NavisworksTransport
|
||||
Distance = clashResult.Distance,
|
||||
CreatedTime = DateTime.Now,
|
||||
// 🔥 使用ClashDetective确认时的位置信息
|
||||
Item1Position = confirmedPosition?.Item1Position,
|
||||
AnimatedObjectTrackedPosition = confirmedPosition?.AnimatedObjectTrackedPosition,
|
||||
Item2Position = confirmedPosition?.Item2Position,
|
||||
Item1YawRadians = confirmedPosition?.Item1YawRadians ?? 0,
|
||||
Item1Rotation = confirmedPosition?.Item1Rotation,
|
||||
Item1HasCustomRotation = confirmedPosition?.Item1HasCustomRotation ?? false,
|
||||
AnimatedObjectTrackedYawRadians = confirmedPosition?.AnimatedObjectTrackedYawRadians ?? 0,
|
||||
AnimatedObjectTrackedRotation = confirmedPosition?.AnimatedObjectTrackedRotation,
|
||||
AnimatedObjectHasTrackedRotation = confirmedPosition?.AnimatedObjectHasTrackedRotation ?? false,
|
||||
HasPositionInfo = confirmedPosition != null
|
||||
};
|
||||
clashResults.Add(collisionResult);
|
||||
@ -1056,7 +1062,7 @@ namespace NavisworksTransport
|
||||
.ToList();
|
||||
|
||||
// 🔥 日志:统计位置信息保留情况
|
||||
var withPositionCount = finalClashResults.Count(c => c.HasPositionInfo && c.Item1Position != null);
|
||||
var withPositionCount = finalClashResults.Count(c => c.HasPositionInfo && c.AnimatedObjectTrackedPosition != null);
|
||||
LogManager.Info($"[最终去重] ClashDetective结果去重: {clashResults.Count} 个碰撞 -> {finalClashResults.Count} 个唯一碰撞对,其中 {withPositionCount} 个包含位置信息");
|
||||
|
||||
// 🔥 优化:在去重后为结果设置详细的 DisplayName
|
||||
@ -1132,7 +1138,7 @@ namespace NavisworksTransport
|
||||
LogManager.Info($"[预计算数据] 共有 {precomputedCollisions.Count} 个碰撞记录");
|
||||
|
||||
// 检查预计算碰撞结果是否包含位置信息
|
||||
var collisionsWithPosition = precomputedCollisions.Count(c => c.HasPositionInfo && c.Item1Position != null);
|
||||
var collisionsWithPosition = precomputedCollisions.Count(c => c.HasPositionInfo && c.AnimatedObjectTrackedPosition != null);
|
||||
LogManager.Info($"[预计算数据] 包含位置信息的碰撞: {collisionsWithPosition}/{precomputedCollisions.Count}");
|
||||
|
||||
// 直接使用所有预计算结果,只过滤有效对象(不去重)
|
||||
@ -2168,11 +2174,11 @@ namespace NavisworksTransport
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
||||
// 位置和朝向信息用于还原碰撞场景
|
||||
public Point3D Item1Position { get; set; }
|
||||
public Point3D AnimatedObjectTrackedPosition { get; set; }
|
||||
public Point3D Item2Position { get; set; }
|
||||
public double Item1YawRadians { get; set; } // 运动物体朝向(弧度)
|
||||
public Rotation3D Item1Rotation { get; set; } // 运动物体完整三维姿态
|
||||
public bool Item1HasCustomRotation { get; set; }
|
||||
public double AnimatedObjectTrackedYawRadians { get; set; } // 动画跟踪点对应的运动物体朝向(弧度)
|
||||
public Rotation3D AnimatedObjectTrackedRotation { get; set; } // 动画跟踪点对应的运动物体完整三维姿态
|
||||
public bool AnimatedObjectHasTrackedRotation { get; set; }
|
||||
public bool HasPositionInfo { get; set; }
|
||||
|
||||
// IEquatable<CollisionResult> 实现:基于碰撞对象进行去重
|
||||
|
||||
@ -653,14 +653,14 @@ namespace NavisworksTransport.Core
|
||||
var collisions = new List<CollisionResult>();
|
||||
foreach (var obj in collisionObjects)
|
||||
{
|
||||
if (obj.Item1PosX.HasValue && obj.Item1PosY.HasValue && obj.Item1PosZ.HasValue)
|
||||
if (obj.AnimatedObjectTrackedPosX.HasValue && obj.AnimatedObjectTrackedPosY.HasValue && obj.AnimatedObjectTrackedPosZ.HasValue)
|
||||
{
|
||||
collisions.Add(new CollisionResult
|
||||
{
|
||||
Center = new Point3D(
|
||||
obj.Item1PosX.Value,
|
||||
obj.Item1PosY.Value,
|
||||
obj.Item1PosZ.Value),
|
||||
obj.AnimatedObjectTrackedPosX.Value,
|
||||
obj.AnimatedObjectTrackedPosY.Value,
|
||||
obj.AnimatedObjectTrackedPosZ.Value),
|
||||
Item2 = ModelItemProxyHelper.CreateProxy(obj.DisplayName)
|
||||
});
|
||||
}
|
||||
|
||||
@ -234,16 +234,16 @@ namespace NavisworksTransport
|
||||
PathId TEXT,
|
||||
DisplayName TEXT,
|
||||
ObjectName TEXT,
|
||||
-- 碰撞时运动物体的位置和朝向(用于还原碰撞场景)
|
||||
Item1PosX REAL,
|
||||
Item1PosY REAL,
|
||||
Item1PosZ REAL,
|
||||
Item1YawRadians REAL,
|
||||
Item1RotA REAL,
|
||||
Item1RotB REAL,
|
||||
Item1RotC REAL,
|
||||
Item1RotD REAL,
|
||||
Item1HasCustomRotation INTEGER DEFAULT 0,
|
||||
-- 碰撞时运动物体的动画跟踪点位置和姿态(用于还原碰撞场景)
|
||||
AnimatedObjectTrackedPosX REAL,
|
||||
AnimatedObjectTrackedPosY REAL,
|
||||
AnimatedObjectTrackedPosZ REAL,
|
||||
AnimatedObjectTrackedYawRadians REAL,
|
||||
AnimatedObjectTrackedRotA REAL,
|
||||
AnimatedObjectTrackedRotB REAL,
|
||||
AnimatedObjectTrackedRotC REAL,
|
||||
AnimatedObjectTrackedRotD REAL,
|
||||
AnimatedObjectHasTrackedRotation INTEGER DEFAULT 0,
|
||||
HasPositionInfo INTEGER DEFAULT 0,
|
||||
FOREIGN KEY(DetectionRecordId) REFERENCES CollisionDetectionRecords(Id) ON DELETE CASCADE
|
||||
)
|
||||
@ -300,8 +300,8 @@ namespace NavisworksTransport
|
||||
|
||||
// 12. 设置数据库版本(SQLite内置user_version)
|
||||
// 版本号格式:主版本*10000 + 次版本*100 + 修订号
|
||||
// 例如:2.1.4 = 20104
|
||||
ExecuteNonQuery("PRAGMA user_version = 20104"); // v2.1.4 - ClashDetective碰撞对象位姿字段扩展与Rail三维恢复链路调整
|
||||
// 例如:2.1.5 = 20105
|
||||
ExecuteNonQuery("PRAGMA user_version = 20105"); // v2.1.5 - 碰撞对象位姿字段命名统一为动画跟踪点语义
|
||||
|
||||
// 创建索引
|
||||
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_reports_route ON CollisionReports(RouteId)");
|
||||
@ -1114,11 +1114,11 @@ namespace NavisworksTransport
|
||||
var sql = @"
|
||||
INSERT INTO ClashDetectiveCollisionObjects
|
||||
(DetectionRecordId, ModelIndex, PathId, DisplayName, ObjectName,
|
||||
Item1PosX, Item1PosY, Item1PosZ, Item1YawRadians,
|
||||
Item1RotA, Item1RotB, Item1RotC, Item1RotD, Item1HasCustomRotation, HasPositionInfo)
|
||||
AnimatedObjectTrackedPosX, AnimatedObjectTrackedPosY, AnimatedObjectTrackedPosZ, AnimatedObjectTrackedYawRadians,
|
||||
AnimatedObjectTrackedRotA, AnimatedObjectTrackedRotB, AnimatedObjectTrackedRotC, AnimatedObjectTrackedRotD, AnimatedObjectHasTrackedRotation, HasPositionInfo)
|
||||
VALUES (@detectionRecordId, @modelIndex, @pathId, @displayName, @objectName,
|
||||
@item1PosX, @item1PosY, @item1PosZ, @item1YawRadians,
|
||||
@item1RotA, @item1RotB, @item1RotC, @item1RotD, @item1HasCustomRotation, @hasPositionInfo)
|
||||
@trackedPosX, @trackedPosY, @trackedPosZ, @trackedYawRadians,
|
||||
@trackedRotA, @trackedRotB, @trackedRotC, @trackedRotD, @hasTrackedRotation, @hasPositionInfo)
|
||||
";
|
||||
|
||||
foreach (var obj in objects)
|
||||
@ -1130,15 +1130,15 @@ namespace NavisworksTransport
|
||||
cmd.Parameters.AddWithValue("@pathId", obj.PathId ?? (object)DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@displayName", obj.DisplayName ?? "");
|
||||
cmd.Parameters.AddWithValue("@objectName", obj.ObjectName ?? "");
|
||||
cmd.Parameters.AddWithValue("@item1PosX", obj.Item1PosX.HasValue ? (object)obj.Item1PosX.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1PosY", obj.Item1PosY.HasValue ? (object)obj.Item1PosY.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1PosZ", obj.Item1PosZ.HasValue ? (object)obj.Item1PosZ.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1YawRadians", obj.Item1YawRadians.HasValue ? (object)obj.Item1YawRadians.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1RotA", obj.Item1RotA.HasValue ? (object)obj.Item1RotA.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1RotB", obj.Item1RotB.HasValue ? (object)obj.Item1RotB.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1RotC", obj.Item1RotC.HasValue ? (object)obj.Item1RotC.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1RotD", obj.Item1RotD.HasValue ? (object)obj.Item1RotD.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@item1HasCustomRotation", obj.Item1HasCustomRotation ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@trackedPosX", obj.AnimatedObjectTrackedPosX.HasValue ? (object)obj.AnimatedObjectTrackedPosX.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@trackedPosY", obj.AnimatedObjectTrackedPosY.HasValue ? (object)obj.AnimatedObjectTrackedPosY.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@trackedPosZ", obj.AnimatedObjectTrackedPosZ.HasValue ? (object)obj.AnimatedObjectTrackedPosZ.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@trackedYawRadians", obj.AnimatedObjectTrackedYawRadians.HasValue ? (object)obj.AnimatedObjectTrackedYawRadians.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@trackedRotA", obj.AnimatedObjectTrackedRotA.HasValue ? (object)obj.AnimatedObjectTrackedRotA.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@trackedRotB", obj.AnimatedObjectTrackedRotB.HasValue ? (object)obj.AnimatedObjectTrackedRotB.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@trackedRotC", obj.AnimatedObjectTrackedRotC.HasValue ? (object)obj.AnimatedObjectTrackedRotC.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@trackedRotD", obj.AnimatedObjectTrackedRotD.HasValue ? (object)obj.AnimatedObjectTrackedRotD.Value : DBNull.Value);
|
||||
cmd.Parameters.AddWithValue("@hasTrackedRotation", obj.AnimatedObjectHasTrackedRotation ? 1 : 0);
|
||||
cmd.Parameters.AddWithValue("@hasPositionInfo", obj.HasPositionInfo ? 1 : 0);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
@ -1166,8 +1166,8 @@ namespace NavisworksTransport
|
||||
{
|
||||
var sql = @"
|
||||
SELECT Id, DetectionRecordId, ModelIndex, PathId, DisplayName, ObjectName,
|
||||
Item1PosX, Item1PosY, Item1PosZ, Item1YawRadians,
|
||||
Item1RotA, Item1RotB, Item1RotC, Item1RotD, Item1HasCustomRotation, HasPositionInfo
|
||||
AnimatedObjectTrackedPosX, AnimatedObjectTrackedPosY, AnimatedObjectTrackedPosZ, AnimatedObjectTrackedYawRadians,
|
||||
AnimatedObjectTrackedRotA, AnimatedObjectTrackedRotB, AnimatedObjectTrackedRotC, AnimatedObjectTrackedRotD, AnimatedObjectHasTrackedRotation, HasPositionInfo
|
||||
FROM ClashDetectiveCollisionObjects
|
||||
WHERE DetectionRecordId = @detectionRecordId
|
||||
";
|
||||
@ -1187,15 +1187,15 @@ namespace NavisworksTransport
|
||||
PathId = reader["PathId"] != DBNull.Value ? reader["PathId"].ToString() : null,
|
||||
DisplayName = reader["DisplayName"]?.ToString(),
|
||||
ObjectName = reader["ObjectName"]?.ToString(),
|
||||
Item1PosX = reader["Item1PosX"] != DBNull.Value ? Convert.ToDouble(reader["Item1PosX"]) : (double?)null,
|
||||
Item1PosY = reader["Item1PosY"] != DBNull.Value ? Convert.ToDouble(reader["Item1PosY"]) : (double?)null,
|
||||
Item1PosZ = reader["Item1PosZ"] != DBNull.Value ? Convert.ToDouble(reader["Item1PosZ"]) : (double?)null,
|
||||
Item1YawRadians = reader["Item1YawRadians"] != DBNull.Value ? Convert.ToDouble(reader["Item1YawRadians"]) : (double?)null,
|
||||
Item1RotA = reader["Item1RotA"] != DBNull.Value ? Convert.ToDouble(reader["Item1RotA"]) : (double?)null,
|
||||
Item1RotB = reader["Item1RotB"] != DBNull.Value ? Convert.ToDouble(reader["Item1RotB"]) : (double?)null,
|
||||
Item1RotC = reader["Item1RotC"] != DBNull.Value ? Convert.ToDouble(reader["Item1RotC"]) : (double?)null,
|
||||
Item1RotD = reader["Item1RotD"] != DBNull.Value ? Convert.ToDouble(reader["Item1RotD"]) : (double?)null,
|
||||
Item1HasCustomRotation = reader["Item1HasCustomRotation"] != DBNull.Value && Convert.ToInt32(reader["Item1HasCustomRotation"]) == 1,
|
||||
AnimatedObjectTrackedPosX = reader["AnimatedObjectTrackedPosX"] != DBNull.Value ? Convert.ToDouble(reader["AnimatedObjectTrackedPosX"]) : (double?)null,
|
||||
AnimatedObjectTrackedPosY = reader["AnimatedObjectTrackedPosY"] != DBNull.Value ? Convert.ToDouble(reader["AnimatedObjectTrackedPosY"]) : (double?)null,
|
||||
AnimatedObjectTrackedPosZ = reader["AnimatedObjectTrackedPosZ"] != DBNull.Value ? Convert.ToDouble(reader["AnimatedObjectTrackedPosZ"]) : (double?)null,
|
||||
AnimatedObjectTrackedYawRadians = reader["AnimatedObjectTrackedYawRadians"] != DBNull.Value ? Convert.ToDouble(reader["AnimatedObjectTrackedYawRadians"]) : (double?)null,
|
||||
AnimatedObjectTrackedRotA = reader["AnimatedObjectTrackedRotA"] != DBNull.Value ? Convert.ToDouble(reader["AnimatedObjectTrackedRotA"]) : (double?)null,
|
||||
AnimatedObjectTrackedRotB = reader["AnimatedObjectTrackedRotB"] != DBNull.Value ? Convert.ToDouble(reader["AnimatedObjectTrackedRotB"]) : (double?)null,
|
||||
AnimatedObjectTrackedRotC = reader["AnimatedObjectTrackedRotC"] != DBNull.Value ? Convert.ToDouble(reader["AnimatedObjectTrackedRotC"]) : (double?)null,
|
||||
AnimatedObjectTrackedRotD = reader["AnimatedObjectTrackedRotD"] != DBNull.Value ? Convert.ToDouble(reader["AnimatedObjectTrackedRotD"]) : (double?)null,
|
||||
AnimatedObjectHasTrackedRotation = reader["AnimatedObjectHasTrackedRotation"] != DBNull.Value && Convert.ToInt32(reader["AnimatedObjectHasTrackedRotation"]) == 1,
|
||||
HasPositionInfo = reader["HasPositionInfo"] != DBNull.Value && Convert.ToInt32(reader["HasPositionInfo"]) == 1
|
||||
});
|
||||
}
|
||||
@ -3561,16 +3561,16 @@ namespace NavisworksTransport
|
||||
public string DisplayName { get; set; }
|
||||
public string ObjectName { get; set; }
|
||||
|
||||
// 碰撞时运动物体的位置和朝向(用于还原碰撞场景)
|
||||
public double? Item1PosX { get; set; }
|
||||
public double? Item1PosY { get; set; }
|
||||
public double? Item1PosZ { get; set; }
|
||||
public double? Item1YawRadians { get; set; }
|
||||
public double? Item1RotA { get; set; }
|
||||
public double? Item1RotB { get; set; }
|
||||
public double? Item1RotC { get; set; }
|
||||
public double? Item1RotD { get; set; }
|
||||
public bool Item1HasCustomRotation { get; set; }
|
||||
// 碰撞时运动物体的动画跟踪点位置和姿态(用于还原碰撞场景)
|
||||
public double? AnimatedObjectTrackedPosX { get; set; }
|
||||
public double? AnimatedObjectTrackedPosY { get; set; }
|
||||
public double? AnimatedObjectTrackedPosZ { get; set; }
|
||||
public double? AnimatedObjectTrackedYawRadians { get; set; }
|
||||
public double? AnimatedObjectTrackedRotA { get; set; }
|
||||
public double? AnimatedObjectTrackedRotB { get; set; }
|
||||
public double? AnimatedObjectTrackedRotC { get; set; }
|
||||
public double? AnimatedObjectTrackedRotD { get; set; }
|
||||
public bool AnimatedObjectHasTrackedRotation { get; set; }
|
||||
public bool HasPositionInfo { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ using NavisworksTransport.Commands;
|
||||
using NavisworksTransport.UI.WPF.Collections;
|
||||
using NavisworksTransport.UI.WPF.Views;
|
||||
using NavisworksTransport.Utils;
|
||||
using NavisworksTransport.Utils.CoordinateSystem;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
@ -178,11 +179,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
public ICommand HighlightCommand { get; set; }
|
||||
public ICommand ClearHighlightCommand { get; set; }
|
||||
|
||||
// 碰撞位置信息(用于还原碰撞场景)
|
||||
public Point3D Item1Position { get; set; }
|
||||
public double Item1YawRadians { get; set; }
|
||||
public Rotation3D Item1Rotation { get; set; }
|
||||
public bool Item1HasCustomRotation { get; set; }
|
||||
// 动画跟踪点位置信息(用于还原碰撞场景)
|
||||
public Point3D AnimatedObjectTrackedPosition { get; set; }
|
||||
public double AnimatedObjectTrackedYawRadians { get; set; }
|
||||
public Rotation3D AnimatedObjectTrackedRotation { get; set; }
|
||||
public bool AnimatedObjectHasTrackedRotation { get; set; }
|
||||
public bool HasPositionInfo { get; set; }
|
||||
}
|
||||
|
||||
@ -1700,9 +1701,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 2. 先保存物体原始尺寸(在设置SelectedAnimatedObject之前)
|
||||
var bbox = newObject.BoundingBox();
|
||||
double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor();
|
||||
_objectOriginalLength = (bbox.Max.X - bbox.Min.X) / metersToModelUnits;
|
||||
_objectOriginalWidth = (bbox.Max.Y - bbox.Min.Y) / metersToModelUnits;
|
||||
_objectOriginalHeight = (bbox.Max.Z - bbox.Min.Z) / metersToModelUnits;
|
||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
var axisConvention = ModelAxisConvention.CreateDefaultForHost(adapter.HostType);
|
||||
double objectLength = axisConvention.GetForwardSize(bbox);
|
||||
double objectWidth = axisConvention.GetSideSize(bbox);
|
||||
double objectHeight = axisConvention.GetUpSize(bbox);
|
||||
_objectOriginalLength = objectLength / metersToModelUnits;
|
||||
_objectOriginalWidth = objectWidth / metersToModelUnits;
|
||||
_objectOriginalHeight = objectHeight / metersToModelUnits;
|
||||
_pathAnimationManager?.SetRealObjectDimensions(objectLength, objectWidth, objectHeight);
|
||||
LogManager.Debug($"[选择物体] 保存原始尺寸: 长度={_objectOriginalLength:F2}m, 宽度={_objectOriginalWidth:F2}m, 高度={_objectOriginalHeight:F2}m");
|
||||
|
||||
// 3. 设置新物体(会触发UpdatePassageSpaceVisualization)
|
||||
@ -2508,10 +2515,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
CollisionSceneHelper.MoveToCollisionAndFocus(
|
||||
collisionObject.ModelItem,
|
||||
null,
|
||||
collisionObject.Item1Position,
|
||||
collisionObject.Item1YawRadians,
|
||||
collisionObject.Item1Rotation,
|
||||
collisionObject.Item1HasCustomRotation,
|
||||
collisionObject.AnimatedObjectTrackedPosition,
|
||||
collisionObject.AnimatedObjectTrackedYawRadians,
|
||||
collisionObject.AnimatedObjectTrackedRotation,
|
||||
collisionObject.AnimatedObjectHasTrackedRotation,
|
||||
false);
|
||||
return;
|
||||
}
|
||||
@ -2548,10 +2555,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
CollisionSceneHelper.MoveToCollisionAndFocus(
|
||||
collisionObject.ModelItem,
|
||||
animatedObject,
|
||||
collisionObject.Item1Position,
|
||||
collisionObject.Item1YawRadians,
|
||||
collisionObject.Item1Rotation,
|
||||
collisionObject.Item1HasCustomRotation,
|
||||
collisionObject.AnimatedObjectTrackedPosition,
|
||||
collisionObject.AnimatedObjectTrackedYawRadians,
|
||||
collisionObject.AnimatedObjectTrackedRotation,
|
||||
collisionObject.AnimatedObjectHasTrackedRotation,
|
||||
collisionObject.HasPositionInfo);
|
||||
|
||||
// 🔥 虚拟物体缩放已通过MoveItemToPositionAndYawWithCurrentScale保留
|
||||
@ -3680,21 +3687,21 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
ModelItem = modelItem,
|
||||
HighlightCommand = new RelayCommand(() => resultViewModel.ExecuteHighlightCollisionObject(objViewModel)),
|
||||
// 填充碰撞位置信息
|
||||
Item1Position = objRecord.HasPositionInfo && objRecord.Item1PosX.HasValue ?
|
||||
new Point3D(objRecord.Item1PosX.Value, objRecord.Item1PosY.Value, objRecord.Item1PosZ.Value) : null,
|
||||
Item1YawRadians = objRecord.Item1YawRadians ?? 0,
|
||||
Item1Rotation = objRecord.Item1HasCustomRotation &&
|
||||
objRecord.Item1RotA.HasValue &&
|
||||
objRecord.Item1RotB.HasValue &&
|
||||
objRecord.Item1RotC.HasValue &&
|
||||
objRecord.Item1RotD.HasValue
|
||||
AnimatedObjectTrackedPosition = objRecord.HasPositionInfo && objRecord.AnimatedObjectTrackedPosX.HasValue ?
|
||||
new Point3D(objRecord.AnimatedObjectTrackedPosX.Value, objRecord.AnimatedObjectTrackedPosY.Value, objRecord.AnimatedObjectTrackedPosZ.Value) : null,
|
||||
AnimatedObjectTrackedYawRadians = objRecord.AnimatedObjectTrackedYawRadians ?? 0,
|
||||
AnimatedObjectTrackedRotation = objRecord.AnimatedObjectHasTrackedRotation &&
|
||||
objRecord.AnimatedObjectTrackedRotA.HasValue &&
|
||||
objRecord.AnimatedObjectTrackedRotB.HasValue &&
|
||||
objRecord.AnimatedObjectTrackedRotC.HasValue &&
|
||||
objRecord.AnimatedObjectTrackedRotD.HasValue
|
||||
? new Rotation3D(
|
||||
objRecord.Item1RotA.Value,
|
||||
objRecord.Item1RotB.Value,
|
||||
objRecord.Item1RotC.Value,
|
||||
objRecord.Item1RotD.Value)
|
||||
objRecord.AnimatedObjectTrackedRotA.Value,
|
||||
objRecord.AnimatedObjectTrackedRotB.Value,
|
||||
objRecord.AnimatedObjectTrackedRotC.Value,
|
||||
objRecord.AnimatedObjectTrackedRotD.Value)
|
||||
: Rotation3D.Identity,
|
||||
Item1HasCustomRotation = objRecord.Item1HasCustomRotation,
|
||||
AnimatedObjectHasTrackedRotation = objRecord.AnimatedObjectHasTrackedRotation,
|
||||
HasPositionInfo = objRecord.HasPositionInfo
|
||||
};
|
||||
resultViewModel.CollisionObjects.Add(objViewModel);
|
||||
@ -4410,9 +4417,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
else // Rail
|
||||
{
|
||||
// 空轨路径:物体的长度方向(X轴,前进方向)朝向路径方向
|
||||
// 通行空间沿路径方向 = X方向尺寸(长度),垂直于路径方向 = Y方向尺寸(宽度),高度上下都加间隙(在空中)
|
||||
// 通行空间沿路径方向 = X方向尺寸(长度),垂直于路径方向 = Y方向尺寸(宽度)
|
||||
// 法线方向只在远离轨道的一侧留间隙:
|
||||
// - 轨下安装:顶面贴路径,底面留间隙
|
||||
// - 轨上安装:底面贴路径,顶面留间隙
|
||||
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
|
||||
passageNormalToPath = virtualObjectHeight + 2 * safetyMargin; // Z方向(高度)+ 2*间隙(法线方向,上下都加)
|
||||
passageNormalToPath = virtualObjectHeight + safetyMargin; // 法线方向单侧留间隙
|
||||
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
|
||||
// 空轨路径不需要分段参数
|
||||
passageNormalToPathVertical = passageNormalToPath;
|
||||
@ -4422,13 +4432,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
else if (SelectedAnimatedObject != null)
|
||||
{
|
||||
// 使用选择物体的包围盒尺寸(模型单位)
|
||||
// 使用选择物体的局部轴语义尺寸(模型单位)
|
||||
var bbox = SelectedAnimatedObject.BoundingBox();
|
||||
|
||||
// 包围盒尺寸已经是模型单位
|
||||
double sizeX = bbox.Max.X - bbox.Min.X; // X方向 = 长度(前进方向)
|
||||
double sizeY = bbox.Max.Y - bbox.Min.Y; // Y方向 = 宽度(侧面)
|
||||
double sizeZ = bbox.Max.Z - bbox.Min.Z; // Z方向 = 高度
|
||||
var adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
var axisConvention = ModelAxisConvention.CreateDefaultForHost(adapter.HostType);
|
||||
double sizeAlongPath = axisConvention.GetForwardSize(bbox);
|
||||
double sizeAcrossPath = axisConvention.GetSideSize(bbox);
|
||||
double sizeNormalToPath = axisConvention.GetUpSize(bbox);
|
||||
|
||||
// 根据路径类型确定通行空间的尺寸
|
||||
if (CurrentPathRoute.PathType == NavisworksTransport.PathType.Rail || CurrentPathRoute.PathType == NavisworksTransport.PathType.Hoisting)
|
||||
@ -4436,12 +4446,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 空中路径(空轨或吊装):
|
||||
// 高度上下都加间隙
|
||||
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
|
||||
passageNormalToPath = sizeZ + 2 * safetyMargin; // Z方向(高度)+ 2*间隙(法线方向)
|
||||
passageNormalToPath = sizeNormalToPath + 2 * safetyMargin; // 局部up方向高度 + 2*间隙(法线方向)
|
||||
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
|
||||
// 吊装路径分段参数
|
||||
passageNormalToPathVertical = effectiveLength + 2 * safetyMargin; // 垂直段:物体长度 + 2*间隙
|
||||
passageNormalToPathHorizontal = sizeZ + 2 * safetyMargin; // 水平段:物体高度 + 2*间隙
|
||||
LogManager.Debug($"[通行空间可视化] 空中路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): 有效长度={effectiveLength / metersToUnitsPassage:F2}m, 有效宽度={effectiveWidth / metersToUnitsPassage:F2}m, 高度={sizeZ / metersToUnitsPassage:F2}m, 通行空间沿路径={passageAlongPath / metersToUnitsPassage:F2}m, 垂直路径={passageAcrossPath / metersToUnitsPassage:F2}m, 法线={passageNormalToPath / metersToUnitsPassage:F2}m");
|
||||
passageNormalToPathHorizontal = sizeNormalToPath + 2 * safetyMargin; // 水平段:物体高度 + 2*间隙
|
||||
LogManager.Debug($"[通行空间可视化] 空中路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): 局部长度={sizeAlongPath / metersToUnitsPassage:F2}m, 局部宽度={sizeAcrossPath / metersToUnitsPassage:F2}m, 局部高度={sizeNormalToPath / metersToUnitsPassage:F2}m, 有效长度={effectiveLength / metersToUnitsPassage:F2}m, 有效宽度={effectiveWidth / metersToUnitsPassage:F2}m, 通行空间沿路径={passageAlongPath / metersToUnitsPassage:F2}m, 垂直路径={passageAcrossPath / metersToUnitsPassage:F2}m, 法线={passageNormalToPath / metersToUnitsPassage:F2}m");
|
||||
}
|
||||
else if (CurrentPathRoute.PathType == NavisworksTransport.PathType.Ground)
|
||||
{
|
||||
@ -4449,24 +4459,27 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 物体的长度方向(X轴,前进方向)朝向路径方向
|
||||
// 通行空间沿路径方向 = X方向尺寸(长度),垂直于路径方向 = Y方向尺寸(宽度),高度上方加间隙(下方不需要,因为有地面)
|
||||
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
|
||||
passageNormalToPath = sizeZ + safetyMargin; // Z方向(高度)+ 间隙(法线方向,只有上方)
|
||||
passageNormalToPath = sizeNormalToPath + safetyMargin; // 局部up方向高度 + 间隙(法线方向,只有上方)
|
||||
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
|
||||
// 地面路径不需要分段参数
|
||||
passageNormalToPathVertical = passageNormalToPath;
|
||||
passageNormalToPathHorizontal = passageNormalToPath;
|
||||
LogManager.Debug($"[通行空间可视化] 地面路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): 有效长度={effectiveLength / metersToUnitsPassage:F2}m, 有效宽度={effectiveWidth / metersToUnitsPassage:F2}m, 通行空间沿路径={passageAlongPath / metersToUnitsPassage:F2}m, 垂直路径={passageAcrossPath / metersToUnitsPassage:F2}m, 法线={passageNormalToPath / metersToUnitsPassage:F2}m");
|
||||
LogManager.Debug($"[通行空间可视化] 地面路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): 局部长度={sizeAlongPath / metersToUnitsPassage:F2}m, 局部宽度={sizeAcrossPath / metersToUnitsPassage:F2}m, 局部高度={sizeNormalToPath / metersToUnitsPassage:F2}m, 有效长度={effectiveLength / metersToUnitsPassage:F2}m, 有效宽度={effectiveWidth / metersToUnitsPassage:F2}m, 通行空间沿路径={passageAlongPath / metersToUnitsPassage:F2}m, 垂直路径={passageAcrossPath / metersToUnitsPassage:F2}m, 法线={passageNormalToPath / metersToUnitsPassage:F2}m");
|
||||
}
|
||||
else // Rail
|
||||
{
|
||||
// 空轨路径(可能有坡度):
|
||||
// 物体的长度方向(X轴,前进方向)朝向路径方向
|
||||
// 通行空间沿路径方向 = X方向尺寸(长度),垂直于路径方向 = Y方向尺寸(宽度),高度上下都加间隙(在空中)
|
||||
// 通行空间沿路径方向 = X方向尺寸(长度),垂直于路径方向 = Y方向尺寸(宽度)
|
||||
// 法线方向只在远离轨道的一侧留间隙:
|
||||
// - 轨下安装:顶面贴路径,底面留间隙
|
||||
// - 轨上安装:底面贴路径,顶面留间隙
|
||||
passageAcrossPath = effectiveWidth + 2 * safetyMargin; // 旋转后宽度 + 2*间隙(垂直于路径方向)
|
||||
passageNormalToPath = sizeZ + 2 * safetyMargin; // Z方向(高度)+ 2*间隙(法线方向,上下都加)
|
||||
passageNormalToPath = sizeNormalToPath + safetyMargin; // 局部up方向高度 + 单侧间隙(法线方向)
|
||||
passageAlongPath = effectiveLength; // 旋转后长度(沿路径方向)
|
||||
passageNormalToPathVertical = passageNormalToPath;
|
||||
passageNormalToPathHorizontal = passageNormalToPath;
|
||||
LogManager.Debug($"[通行空间可视化] 空轨路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): 有效长度={effectiveLength / metersToUnitsPassage:F2}m, 有效宽度={effectiveWidth / metersToUnitsPassage:F2}m, 通行空间沿路径={passageAlongPath / metersToUnitsPassage:F2}m, 垂直路径={passageAcrossPath / metersToUnitsPassage:F2}m, 法线={passageNormalToPath / metersToUnitsPassage:F2}m");
|
||||
LogManager.Debug($"[通行空间可视化] 空轨路径使用物体尺寸 ({SelectedAnimatedObject.DisplayName}): 局部长度={sizeAlongPath / metersToUnitsPassage:F2}m, 局部宽度={sizeAcrossPath / metersToUnitsPassage:F2}m, 局部高度={sizeNormalToPath / metersToUnitsPassage:F2}m, 有效长度={effectiveLength / metersToUnitsPassage:F2}m, 有效宽度={effectiveWidth / metersToUnitsPassage:F2}m, 通行空间沿路径={passageAlongPath / metersToUnitsPassage:F2}m, 垂直路径={passageAcrossPath / metersToUnitsPassage:F2}m, 法线={passageNormalToPath / metersToUnitsPassage:F2}m");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@ -1389,7 +1389,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
var collisionData = collisionItem.CollisionData;
|
||||
|
||||
// 首次查看碰撞时保存运动物体状态
|
||||
if (collisionData.Item1 != null && collisionData.HasPositionInfo && collisionData.Item1Position != null)
|
||||
if (collisionData.Item1 != null && collisionData.HasPositionInfo && collisionData.AnimatedObjectTrackedPosition != null)
|
||||
{
|
||||
if (_savedAnimatedObjectState == null || _currentAnimatedObject != collisionData.Item1)
|
||||
{
|
||||
@ -1432,4 +1432,4 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1508,11 +1508,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
|
||||
HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
ProjectReferenceFrame projectFrame = CreateAssemblyProjectReferenceFrame(adapter);
|
||||
|
||||
var bounds = _assemblyTerminalObject.BoundingBox();
|
||||
Point3D canonicalCenter = adapter.ToCanonicalPoint(bounds.Center);
|
||||
Vector3D canonicalUp = adapter.ToCanonicalVector(
|
||||
ModelItemTransformHelper.GetUpDirectionFromTransform(_assemblyTerminalObject.Transform));
|
||||
ModelItemTransformHelper.GetDirectionFromTransform(
|
||||
_assemblyTerminalObject.Transform,
|
||||
projectFrame.DefaultModelAxisConvention.UpAxis));
|
||||
double direction = GetAssemblyAnchorDirection();
|
||||
double verticalOffset = UnitsConverter.ConvertFromMeters(AssemblyAnchorVerticalOffsetInMeters);
|
||||
|
||||
@ -1652,9 +1655,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
HostCoordinateAdapter adapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
ProjectReferenceFrame projectFrame = CreateAssemblyProjectReferenceFrame(adapter);
|
||||
var bounds = _assemblyTerminalObject.BoundingBox();
|
||||
Vector3D localSize = ModelItemTransformHelper.EstimateLocalBoxSize(bounds, _assemblyTerminalObject.Transform);
|
||||
double suggestedOffset = UnitsConverter.ConvertToMeters(Math.Max(0.0, localSize.Z / 2.0));
|
||||
double suggestedOffset = UnitsConverter.ConvertToMeters(
|
||||
Math.Max(0.0, projectFrame.DefaultModelAxisConvention.GetUpSize(bounds) / 2.0));
|
||||
_assemblyAnchorVerticalOffsetInMeters = suggestedOffset;
|
||||
OnPropertyChanged(nameof(AssemblyAnchorVerticalOffsetInMeters));
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ using System;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using NavisworksTransport.Core;
|
||||
using NavisworksTransport.Core.Animation;
|
||||
using NavisworksTransport.Utils.CoordinateSystem;
|
||||
|
||||
namespace NavisworksTransport.Utils
|
||||
{
|
||||
@ -20,8 +21,8 @@ namespace NavisworksTransport.Utils
|
||||
{
|
||||
if (collision == null) return;
|
||||
MoveToCollisionAndFocus(collision.Item2 ?? collision.Item1, animatedObject,
|
||||
collision.Item1Position, collision.Item1YawRadians, collision.Item1Rotation,
|
||||
collision.Item1HasCustomRotation, collision.HasPositionInfo);
|
||||
collision.AnimatedObjectTrackedPosition, collision.AnimatedObjectTrackedYawRadians, collision.AnimatedObjectTrackedRotation,
|
||||
collision.AnimatedObjectHasTrackedRotation, collision.HasPositionInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -29,14 +30,14 @@ namespace NavisworksTransport.Utils
|
||||
/// </summary>
|
||||
/// <param name="hitObject">被撞对象</param>
|
||||
/// <param name="animatedObject">动画物体(运动物体)</param>
|
||||
/// <param name="item1Position">碰撞位置</param>
|
||||
/// <param name="item1YawRadians">碰撞朝向</param>
|
||||
/// <param name="item1Rotation">碰撞完整姿态</param>
|
||||
/// <param name="item1HasCustomRotation">是否有完整姿态</param>
|
||||
/// <param name="animatedObjectTrackedPosition">动画跟踪点位置</param>
|
||||
/// <param name="animatedObjectTrackedYawRadians">动画跟踪点对应的碰撞朝向</param>
|
||||
/// <param name="animatedObjectTrackedRotation">动画跟踪点对应的碰撞完整姿态</param>
|
||||
/// <param name="animatedObjectHasTrackedRotation">是否有完整姿态</param>
|
||||
/// <param name="hasPositionInfo">是否有位置信息</param>
|
||||
public static void MoveToCollisionAndFocus(ModelItem hitObject, ModelItem animatedObject,
|
||||
Point3D item1Position, double item1YawRadians, Rotation3D item1Rotation,
|
||||
bool item1HasCustomRotation, bool hasPositionInfo)
|
||||
Point3D animatedObjectTrackedPosition, double animatedObjectTrackedYawRadians, Rotation3D animatedObjectTrackedRotation,
|
||||
bool animatedObjectHasTrackedRotation, bool hasPositionInfo)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -55,60 +56,30 @@ namespace NavisworksTransport.Utils
|
||||
}
|
||||
|
||||
// 移动动画物体到碰撞位置
|
||||
if (animatedObject != null && hasPositionInfo && item1Position != null)
|
||||
if (animatedObject != null && hasPositionInfo && animatedObjectTrackedPosition != null)
|
||||
{
|
||||
var pam = PathAnimationManager.GetInstance();
|
||||
bool useAnimationPipeline = pam != null &&
|
||||
pam.ControlsAnimatedObject(animatedObject) &&
|
||||
item1HasCustomRotation &&
|
||||
item1Rotation != null;
|
||||
|
||||
// 检查是否是虚拟物体
|
||||
bool isVirtual = VirtualObjectManager.Instance.IsVirtualObjectActive &&
|
||||
VirtualObjectManager.Instance.CurrentVirtualObject == animatedObject;
|
||||
pam.ControlsAnimatedObject(animatedObject);
|
||||
|
||||
if (useAnimationPipeline)
|
||||
{
|
||||
pam.MoveAnimatedObjectToPose(
|
||||
animatedObject,
|
||||
item1Position,
|
||||
item1YawRadians,
|
||||
item1Rotation,
|
||||
true);
|
||||
animatedObjectTrackedPosition,
|
||||
animatedObjectTrackedYawRadians,
|
||||
animatedObjectTrackedRotation ?? Rotation3D.Identity,
|
||||
animatedObjectHasTrackedRotation && animatedObjectTrackedRotation != null);
|
||||
|
||||
LogManager.Info(string.Format(
|
||||
"运动物体已通过动画链路移动到碰撞位置: 位置=({0:F2}, {1:F2}, {2:F2}), customRotation={3}",
|
||||
item1Position.X, item1Position.Y, item1Position.Z,
|
||||
true));
|
||||
}
|
||||
else if (item1HasCustomRotation && item1Rotation != null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"三维碰撞点恢复必须复用动画主链路,当前对象未受 PathAnimationManager 控制: {animatedObject.DisplayName}");
|
||||
animatedObjectTrackedPosition.X, animatedObjectTrackedPosition.Y, animatedObjectTrackedPosition.Z,
|
||||
animatedObjectHasTrackedRotation && animatedObjectTrackedRotation != null));
|
||||
}
|
||||
else
|
||||
{
|
||||
var bounds = animatedObject.BoundingBox();
|
||||
double halfHeight = (bounds.Max.Z - bounds.Min.Z) / 2.0;
|
||||
var targetGroundPosition = new Point3D(
|
||||
item1Position.X,
|
||||
item1Position.Y,
|
||||
item1Position.Z - halfHeight
|
||||
);
|
||||
|
||||
if (isVirtual)
|
||||
{
|
||||
ModelItemTransformHelper.MoveItemToPositionAndYawWithCurrentScale(
|
||||
animatedObject, targetGroundPosition, item1YawRadians);
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelItemTransformHelper.MoveItemToPositionAndYaw(animatedObject, targetGroundPosition, item1YawRadians);
|
||||
}
|
||||
|
||||
LogManager.Info(string.Format("运动物体已移动到碰撞位置: ({0:F2}, {1:F2}, {2:F2}), 朝向: {3:F2}°",
|
||||
targetGroundPosition.X, targetGroundPosition.Y, targetGroundPosition.Z,
|
||||
item1YawRadians * 180 / Math.PI));
|
||||
throw new InvalidOperationException(
|
||||
$"碰撞点恢复必须复用动画主链路,当前对象未受 PathAnimationManager 控制: {animatedObject.DisplayName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,8 +103,8 @@ namespace NavisworksTransport.Utils
|
||||
// 从PathAnimationManager获取当前实际朝向
|
||||
var pam = PathAnimationManager.GetInstance();
|
||||
var currentYaw = pam.CurrentYaw;
|
||||
var currentRotation = pam.CurrentRotation;
|
||||
var hasCustomRotation = pam.HasCurrentRotation;
|
||||
var currentRotation = pam.TrackedRotation;
|
||||
var hasCustomRotation = pam.HasTrackedRotation;
|
||||
var currentState = pam.GetObjectCurrentPosition(animatedObject);
|
||||
pam.SaveObjectState(animatedObject);
|
||||
|
||||
@ -169,15 +140,18 @@ namespace NavisworksTransport.Utils
|
||||
try
|
||||
{
|
||||
var pam = PathAnimationManager.GetInstance();
|
||||
if (pam != null && pam.ControlsAnimatedObject(animatedObject))
|
||||
{
|
||||
pam.RestoreAnimatedObjectState(animatedObject);
|
||||
LogManager.Info(string.Format(
|
||||
"动画物体已通过PathAnimationManager恢复状态: {0}, customRotation={1}",
|
||||
animatedObject.DisplayName,
|
||||
state.HasCustomRotation));
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.HasCustomRotation)
|
||||
{
|
||||
if (pam != null && ReferenceEquals(pam.AnimatedObject, animatedObject))
|
||||
{
|
||||
pam.RestoreAnimatedObjectState(animatedObject);
|
||||
LogManager.Info(string.Format("动画物体已通过PathAnimationManager恢复三维姿态: {0}", animatedObject.DisplayName));
|
||||
return;
|
||||
}
|
||||
|
||||
bool isVirtualWithCustomRotation = VirtualObjectManager.Instance.IsVirtualObjectActive &&
|
||||
VirtualObjectManager.Instance.CurrentVirtualObject == animatedObject;
|
||||
if (isVirtualWithCustomRotation)
|
||||
|
||||
@ -34,6 +34,7 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
public Vector3 UpUnitVector => _upUnitVector;
|
||||
public Vector3D ForwardVector => ToNavVector(_forwardUnitVector);
|
||||
public Vector3D UpVector => ToNavVector(_upUnitVector);
|
||||
public LocalAxisDirection SideAxis => GetAxisDirection(Normalize(Vector3.Cross(_forwardUnitVector, _upUnitVector)));
|
||||
|
||||
/// <summary>
|
||||
/// 根据模型局部轴约定,直接构造“本地 forward/up 对齐到目标世界 forward/up”的线性姿态。
|
||||
@ -107,6 +108,21 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
}
|
||||
}
|
||||
|
||||
public double GetForwardSize(BoundingBox3D bounds)
|
||||
{
|
||||
return GetAxisExtent(bounds, ForwardAxis);
|
||||
}
|
||||
|
||||
public double GetUpSize(BoundingBox3D bounds)
|
||||
{
|
||||
return GetAxisExtent(bounds, UpAxis);
|
||||
}
|
||||
|
||||
public double GetSideSize(BoundingBox3D bounds)
|
||||
{
|
||||
return GetAxisExtent(bounds, SideAxis);
|
||||
}
|
||||
|
||||
private static Vector3 GetAxisVector3(LocalAxisDirection axis)
|
||||
{
|
||||
switch (axis)
|
||||
@ -229,5 +245,23 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
{
|
||||
return new Vector3D(vector.X, vector.Y, vector.Z);
|
||||
}
|
||||
|
||||
private static double GetAxisExtent(BoundingBox3D bounds, LocalAxisDirection axis)
|
||||
{
|
||||
switch (axis)
|
||||
{
|
||||
case LocalAxisDirection.PositiveX:
|
||||
case LocalAxisDirection.NegativeX:
|
||||
return bounds.Max.X - bounds.Min.X;
|
||||
case LocalAxisDirection.PositiveY:
|
||||
case LocalAxisDirection.NegativeY:
|
||||
return bounds.Max.Y - bounds.Min.Y;
|
||||
case LocalAxisDirection.PositiveZ:
|
||||
case LocalAxisDirection.NegativeZ:
|
||||
return bounds.Max.Z - bounds.Min.Z;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(axis), axis, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,6 +111,22 @@ namespace NavisworksTransport.Utils
|
||||
return GetAxisDirectionFromTransform(transform, 2, new Vector3D(0, 0, 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据模型局部轴约定,获取指定局部轴在世界坐标中的方向。
|
||||
/// </summary>
|
||||
public static Vector3D GetDirectionFromTransform(
|
||||
Transform3D transform,
|
||||
LocalAxisDirection axisDirection)
|
||||
{
|
||||
GetAxisIndexAndSign(axisDirection, out int axisIndex, out int sign);
|
||||
Vector3D fallback = GetFallbackAxis(axisDirection);
|
||||
Vector3D direction = GetAxisDirectionFromTransform(transform, axisIndex, fallback);
|
||||
|
||||
return sign >= 0
|
||||
? direction
|
||||
: new Vector3D(-direction.X, -direction.Y, -direction.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取绑定在物体局部坐标中的稳定底面锚点。
|
||||
/// 适用于带俯仰/侧倾的三维姿态对象,避免使用世界 AABB 底面中心导致锚点漂移。
|
||||
@ -305,6 +321,60 @@ namespace NavisworksTransport.Utils
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetAxisIndexAndSign(LocalAxisDirection axisDirection, out int axisIndex, out int sign)
|
||||
{
|
||||
switch (axisDirection)
|
||||
{
|
||||
case LocalAxisDirection.PositiveX:
|
||||
axisIndex = 0;
|
||||
sign = 1;
|
||||
break;
|
||||
case LocalAxisDirection.NegativeX:
|
||||
axisIndex = 0;
|
||||
sign = -1;
|
||||
break;
|
||||
case LocalAxisDirection.PositiveY:
|
||||
axisIndex = 1;
|
||||
sign = 1;
|
||||
break;
|
||||
case LocalAxisDirection.NegativeY:
|
||||
axisIndex = 1;
|
||||
sign = -1;
|
||||
break;
|
||||
case LocalAxisDirection.PositiveZ:
|
||||
axisIndex = 2;
|
||||
sign = 1;
|
||||
break;
|
||||
case LocalAxisDirection.NegativeZ:
|
||||
axisIndex = 2;
|
||||
sign = -1;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(axisDirection), axisDirection, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static Vector3D GetFallbackAxis(LocalAxisDirection axisDirection)
|
||||
{
|
||||
switch (axisDirection)
|
||||
{
|
||||
case LocalAxisDirection.PositiveX:
|
||||
return new Vector3D(1, 0, 0);
|
||||
case LocalAxisDirection.NegativeX:
|
||||
return new Vector3D(-1, 0, 0);
|
||||
case LocalAxisDirection.PositiveY:
|
||||
return new Vector3D(0, 1, 0);
|
||||
case LocalAxisDirection.NegativeY:
|
||||
return new Vector3D(0, -1, 0);
|
||||
case LocalAxisDirection.PositiveZ:
|
||||
return new Vector3D(0, 0, 1);
|
||||
case LocalAxisDirection.NegativeZ:
|
||||
return new Vector3D(0, 0, -1);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(axisDirection), axisDirection, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static double[] SolveLinearSystem3x3(double[,] matrix, double[] values)
|
||||
{
|
||||
double determinant =
|
||||
@ -538,13 +608,13 @@ namespace NavisworksTransport.Utils
|
||||
actualBounds.Min.Z);
|
||||
|
||||
LogManager.Info(
|
||||
$"[模型增量姿态] {item.DisplayName} 应用后位置: " +
|
||||
$"[模型增量姿态] {item.DisplayName} 立即读回位置(可能滞后): " +
|
||||
$"实际=({actualPosition.X:F3},{actualPosition.Y:F3},{actualPosition.Z:F3}), " +
|
||||
$"期望=({targetPosition.X:F3},{targetPosition.Y:F3},{targetPosition.Z:F3}), " +
|
||||
$"偏差=({actualPosition.X - targetPosition.X:F3},{actualPosition.Y - targetPosition.Y:F3},{actualPosition.Z - targetPosition.Z:F3})");
|
||||
|
||||
LogManager.Info(
|
||||
$"[模型增量姿态] {item.DisplayName} 应用后旋转: " +
|
||||
$"[模型增量姿态] {item.DisplayName} 立即读回旋转(可能滞后/不反映override): " +
|
||||
$"实际X=({actualLinear.Get(0, 0):F4},{actualLinear.Get(1, 0):F4},{actualLinear.Get(2, 0):F4}), " +
|
||||
$"实际Y=({actualLinear.Get(0, 1):F4},{actualLinear.Get(1, 1):F4},{actualLinear.Get(2, 1):F4}), " +
|
||||
$"实际Z=({actualLinear.Get(0, 2):F4},{actualLinear.Get(1, 2):F4},{actualLinear.Get(2, 2):F4}), " +
|
||||
|
||||
Loading…
Reference in New Issue
Block a user