初步实现多层吊装路径
This commit is contained in:
parent
ae845bc571
commit
edcbd0802b
48
.agents/skills/project-tools/README.md
Normal file
48
.agents/skills/project-tools/README.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Project Tools Skill
|
||||
|
||||
## 概述
|
||||
|
||||
本 skill 用于指导 NavisworksTransport 项目中工具类的正确使用。
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
project-tools/
|
||||
├── SKILL.md # 主文档:工具类总览和快速参考
|
||||
├── README.md # 本文件
|
||||
├── utils/ # 各工具类详细文档
|
||||
│ ├── DialogHelper.md # 对话框工具
|
||||
│ ├── UnitsConverter.md # 单位转换工具
|
||||
│ ├── LogManager.md # 日志工具
|
||||
│ └── GeometryHelper.md # 几何计算工具
|
||||
└── examples/ # 使用示例
|
||||
└── common-usage.md # 常见场景示例
|
||||
```
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. **编写代码前**:查看 `SKILL.md` 确定需要的工具类
|
||||
2. **具体使用时**:查看 `utils/` 下的详细文档
|
||||
3. **参考示例**:查看 `examples/` 下的实际代码
|
||||
|
||||
## 核心原则
|
||||
|
||||
> **优先复用现有工具类,禁止重复造轮子**
|
||||
|
||||
### 必须使用的工具类(强制)
|
||||
|
||||
| 场景 | 工具类 | 错误示例 |
|
||||
|------|--------|----------|
|
||||
| 对话框Owner设置 | DialogHelper | `dialog.Owner = Application.Current.MainWindow` |
|
||||
| 单位转换 | UnitsConverter | `value * 0.001` |
|
||||
| 日志记录 | LogManager | `Console.WriteLine(...)` |
|
||||
| 几何计算 | GeometryHelper | `Math.Sqrt(dx*dx + dy*dy)` |
|
||||
|
||||
## 检查清单
|
||||
|
||||
编写代码前,请确认:
|
||||
|
||||
- [ ] 阅读了 `SKILL.md` 中相关工具类的说明
|
||||
- [ ] 查阅了 `utils/` 下的详细文档
|
||||
- [ ] 代码中使用了正确的工具类方法
|
||||
- [ ] 没有重复实现已有功能
|
||||
94
.agents/skills/project-tools/SKILL.md
Normal file
94
.agents/skills/project-tools/SKILL.md
Normal file
@ -0,0 +1,94 @@
|
||||
# NavisworksTransport 项目工具类使用指南
|
||||
|
||||
## 概述
|
||||
|
||||
本skill记录 NavisworksTransport 项目中的所有工具类及其使用方法。在编写代码前,请务必查看此文档,**优先使用现有工具类,避免重复造轮子**。
|
||||
|
||||
## 工具类总览
|
||||
|
||||
| 工具类 | 文件 | 用途 | 优先级 |
|
||||
|--------|------|------|--------|
|
||||
| **DialogHelper** | `src/Utils/DialogHelper.cs` | 对话框Owner设置和置顶显示 | ⭐⭐⭐ 必须 |
|
||||
| **UnitsConverter** | `src/Utils/UnitsConverter.cs` | 单位转换(米↔模型单位) | ⭐⭐⭐ 必须 |
|
||||
| **LogManager** | `src/Utils/LogManager.cs` | 日志记录 | ⭐⭐⭐ 必须 |
|
||||
| **GeometryHelper** | `src/Utils/GeometryHelper.cs` | 3D几何计算 | ⭐⭐⭐ |
|
||||
| **PathHelper** | `src/Utils/PathHelper.cs` | 路径相关工具 | ⭐⭐⭐ |
|
||||
| **CoordinateConverter** | `src/Utils/CoordinateConverter.cs` | 坐标系转换 | ⭐⭐ |
|
||||
| **ModelHighlightHelper** | `src/Utils/ModelHighlightHelper.cs` | 模型高亮显示 | ⭐⭐ |
|
||||
| **NavisworksSelectionHelper** | `src/Utils/NavisworksSelectionHelper.cs` | 选择集操作 | ⭐⭐ |
|
||||
| **ViewpointHelper** | `src/Utils/ViewpointHelper.cs` | 视点操作 | ⭐⭐ |
|
||||
| **SectionClipHelper** | `src/Utils/SectionClipHelper.cs` | 剖切操作 | ⭐ |
|
||||
|
||||
## 快速参考
|
||||
|
||||
### 对话框开发
|
||||
|
||||
```csharp
|
||||
// ❌ 错误:直接设置Owner,可能在Navisworks环境中失败
|
||||
var dialog = new MyDialog();
|
||||
dialog.Owner = Application.Current.MainWindow; // 可能抛出异常!
|
||||
dialog.Show();
|
||||
|
||||
// ✅ 正确:使用 DialogHelper
|
||||
var dialog = new MyDialog();
|
||||
DialogHelper.SetOwnerSafely(dialog); // 自动处理Navisworks环境
|
||||
dialog.Show();
|
||||
```
|
||||
|
||||
### 单位转换
|
||||
|
||||
```csharp
|
||||
// ❌ 错误:硬编码转换因子
|
||||
double meters = modelUnits * 0.001; // 错误!不同文档单位不同
|
||||
|
||||
// ✅ 正确:使用 UnitsConverter
|
||||
double meters = UnitsConverter.ConvertToMeters(modelUnits);
|
||||
double modelUnits = UnitsConverter.ConvertFromMeters(meters);
|
||||
```
|
||||
|
||||
### 日志记录
|
||||
|
||||
```csharp
|
||||
// ❌ 错误:使用Console或直接输出
|
||||
Console.WriteLine($"Error: {ex.Message}"); // 不可见!
|
||||
|
||||
// ✅ 正确:使用 LogManager
|
||||
LogManager.Info("操作成功");
|
||||
LogManager.Warning("警告信息");
|
||||
LogManager.Error("错误信息", exception);
|
||||
```
|
||||
|
||||
## 详细文档
|
||||
|
||||
- [DialogHelper 使用指南](utils/DialogHelper.md)
|
||||
- [UnitsConverter 使用指南](utils/UnitsConverter.md)
|
||||
- [LogManager 使用指南](utils/LogManager.md)
|
||||
- [GeometryHelper 使用指南](utils/GeometryHelper.md)
|
||||
- [ModelHighlightHelper 使用指南](utils/ModelHighlightHelper.md)
|
||||
|
||||
## 使用检查清单
|
||||
|
||||
在编写新功能前,请检查:
|
||||
|
||||
- [ ] 需要显示对话框?→ 使用 **DialogHelper**
|
||||
- [ ] 涉及单位转换?→ 使用 **UnitsConverter**
|
||||
- [ ] 需要记录日志?→ 使用 **LogManager**
|
||||
- [ ] 需要几何计算?→ 使用 **GeometryHelper**
|
||||
- [ ] 需要高亮模型?→ 使用 **ModelHighlightHelper**
|
||||
- [ ] 需要处理选择集?→ 使用 **NavisworksSelectionHelper**
|
||||
|
||||
## 禁止行为
|
||||
|
||||
以下行为在项目中是**禁止**的:
|
||||
|
||||
1. ❌ 直接设置 `Window.Owner = Application.Current.MainWindow`
|
||||
2. ❌ 硬编码单位转换因子(如 `* 0.001`)
|
||||
3. ❌ 使用 `Console.WriteLine` 输出日志
|
||||
4. ❌ 自己实现距离/角度计算(已有 GeometryHelper)
|
||||
5. ❌ 自己实现集合的线程安全包装(使用 ThreadSafeObservableCollection)
|
||||
|
||||
## 扩展阅读
|
||||
|
||||
- 项目规范:`AGENTS.md`
|
||||
- UI开发规范:`doc/guide/design_principles.md`
|
||||
- Navisworks API 使用:`SKILL.md` (nw-api)
|
||||
178
.agents/skills/project-tools/examples/common-usage.md
Normal file
178
.agents/skills/project-tools/examples/common-usage.md
Normal file
@ -0,0 +1,178 @@
|
||||
# 常见使用场景示例
|
||||
|
||||
## 场景1:创建并显示对话框
|
||||
|
||||
```csharp
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
public class MyViewModel
|
||||
{
|
||||
private MyDialog _dialog;
|
||||
|
||||
public void ShowDialog()
|
||||
{
|
||||
// 防止重复打开
|
||||
if (_dialog != null && _dialog.IsVisible)
|
||||
{
|
||||
_dialog.Activate();
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建对话框
|
||||
_dialog = new MyDialog();
|
||||
|
||||
// ✅ 使用 DialogHelper 设置 Owner
|
||||
DialogHelper.SetOwnerSafely(_dialog);
|
||||
|
||||
// 显示非模态对话框
|
||||
_dialog.Show();
|
||||
|
||||
// 清理引用
|
||||
_dialog.Closed += (s, e) => _dialog = null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 场景2:带单位转换的计算
|
||||
|
||||
```csharp
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
public class PathPlanner
|
||||
{
|
||||
/// <summary>
|
||||
/// 设置网格大小
|
||||
/// </summary>
|
||||
/// <param name="cellSizeInMeters">网格单元大小(米)</param>
|
||||
public void SetCellSize(double cellSizeInMeters)
|
||||
{
|
||||
// ✅ 转换为模型单位存储
|
||||
double cellSize = UnitsConverter.ConvertFromMeters(cellSizeInMeters);
|
||||
|
||||
// 后续计算使用 cellSize(模型单位)
|
||||
_grid = new GridMap(cellSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取路径长度(用于显示)
|
||||
/// </summary>
|
||||
public string GetPathLengthDisplay(PathRoute route)
|
||||
{
|
||||
// ✅ 转换为米显示
|
||||
double lengthInMeters = UnitsConverter.ConvertToMeters(route.TotalLength);
|
||||
return $"{lengthInMeters:F2} 米";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 场景3:完整的操作日志记录
|
||||
|
||||
```csharp
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
public class PathService
|
||||
{
|
||||
public async Task<PathRoute> CreatePathAsync(PathParameters parameters)
|
||||
{
|
||||
LogManager.Info("[路径创建] === 开始创建路径 ===");
|
||||
LogManager.Info($"[路径创建] 参数: {parameters}");
|
||||
|
||||
try
|
||||
{
|
||||
// 验证参数
|
||||
if (!ValidateParameters(parameters))
|
||||
{
|
||||
LogManager.Warning("[路径创建] 参数验证失败");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 执行创建
|
||||
var route = await GeneratePathAsync(parameters);
|
||||
|
||||
LogManager.Info($"[路径创建] 成功: {route.Name}, ID: {route.Id}");
|
||||
LogManager.Info($"[路径创建] 长度: {route.TotalLength:F2} 米, 点数: {route.Points.Count}");
|
||||
|
||||
return route;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// ✅ 记录完整异常信息
|
||||
LogManager.Error($"[路径创建] 失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogManager.Info("[路径创建] === 创建完成 ===");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 场景4:几何计算
|
||||
|
||||
```csharp
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
public class PathAnalyzer
|
||||
{
|
||||
/// <summary>
|
||||
/// 计算路径总长度
|
||||
/// </summary>
|
||||
public double CalculateTotalLength(List<Point3D> points)
|
||||
{
|
||||
double totalLength = 0;
|
||||
|
||||
for (int i = 0; i < points.Count - 1; i++)
|
||||
{
|
||||
// ✅ 使用 GeometryHelper
|
||||
totalLength += GeometryHelper.Distance(points[i], points[i + 1]);
|
||||
}
|
||||
|
||||
return totalLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查转弯是否过急
|
||||
/// </summary>
|
||||
public bool IsTurnTooSharp(Point3D prev, Point3D current, Point3D next, double maxAngle)
|
||||
{
|
||||
Vector3D v1 = GeometryHelper.Subtract(current, prev);
|
||||
Vector3D v2 = GeometryHelper.Subtract(next, current);
|
||||
|
||||
double angle = GeometryHelper.AngleBetweenDegrees(v1, v2);
|
||||
|
||||
return angle > maxAngle;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 场景5:确认对话框
|
||||
|
||||
```csharp
|
||||
using NavisworksTransport.Utils;
|
||||
using System.Windows;
|
||||
|
||||
public class PathViewModel
|
||||
{
|
||||
public void DeletePath(PathRoute route)
|
||||
{
|
||||
// ✅ 使用 DialogHelper 显示确认对话框
|
||||
var result = DialogHelper.ShowMessageBox(
|
||||
$"确定要删除路径 \"{route.Name}\" 吗?\n此操作不可撤销。",
|
||||
"确认删除",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning
|
||||
);
|
||||
|
||||
if (result != MessageBoxResult.Yes)
|
||||
{
|
||||
LogManager.Info($"[删除] 用户取消删除: {route.Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行删除
|
||||
_pathPlanningManager.DeleteRoute(route.Id);
|
||||
LogManager.Info($"[删除] 路径已删除: {route.Name}");
|
||||
}
|
||||
}
|
||||
```
|
||||
127
.agents/skills/project-tools/utils/DialogHelper.md
Normal file
127
.agents/skills/project-tools/utils/DialogHelper.md
Normal file
@ -0,0 +1,127 @@
|
||||
# DialogHelper 使用指南
|
||||
|
||||
## 文件位置
|
||||
|
||||
`src/Utils/DialogHelper.cs`
|
||||
|
||||
## 用途
|
||||
|
||||
统一处理对话框的 Owner 设置和置顶显示,解决 Navisworks 插件环境中的窗口焦点问题。
|
||||
|
||||
## 核心方法
|
||||
|
||||
### 1. SetOwnerSafely - 安全设置Owner(推荐)
|
||||
|
||||
```csharp
|
||||
// 场景:在 ViewModel 中创建对话框
|
||||
var dialog = new MyDialog();
|
||||
DialogHelper.SetOwnerSafely(dialog);
|
||||
dialog.Show(); // 或 dialog.ShowDialog();
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- 自动查找可用的 Owner 窗口
|
||||
- 处理 Navisworks 插件环境的特殊情况
|
||||
- 捕获异常,不会导致程序崩溃
|
||||
|
||||
### 2. ShowDialog - 显示模态对话框(完整处理)
|
||||
|
||||
```csharp
|
||||
// 场景:显示需要返回结果的模态对话框
|
||||
var dialog = new MyInputDialog();
|
||||
bool? result = DialogHelper.ShowDialog(dialog);
|
||||
```
|
||||
|
||||
### 3. ShowMessageBox - 显示消息框
|
||||
|
||||
```csharp
|
||||
// 场景:显示错误提示或确认对话框
|
||||
DialogHelper.ShowMessageBox(
|
||||
"操作成功完成",
|
||||
"提示",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Information
|
||||
);
|
||||
```
|
||||
|
||||
### 4. SetWin32Owner - 强制置顶到Navisworks主窗口
|
||||
|
||||
```csharp
|
||||
// 场景:对话框必须置顶到Navisworks主窗口(极少数情况)
|
||||
var dialog = new MyDialog();
|
||||
DialogHelper.SetWin32Owner(dialog);
|
||||
dialog.Show();
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 示例1:ViewModel中显示非模态对话框
|
||||
|
||||
```csharp
|
||||
private MyDialog _myDialog;
|
||||
|
||||
private void ShowMyDialog()
|
||||
{
|
||||
if (_myDialog != null && _myDialog.IsVisible)
|
||||
{
|
||||
_myDialog.Activate();
|
||||
return;
|
||||
}
|
||||
|
||||
_myDialog = new MyDialog();
|
||||
DialogHelper.SetOwnerSafely(_myDialog); // ✅ 使用工具方法
|
||||
_myDialog.Show();
|
||||
|
||||
_myDialog.Closed += (s, e) => _myDialog = null;
|
||||
}
|
||||
```
|
||||
|
||||
### 示例2:显示确认对话框
|
||||
|
||||
```csharp
|
||||
private bool ConfirmDelete()
|
||||
{
|
||||
var result = DialogHelper.ShowMessageBox(
|
||||
"确定要删除此路径吗?",
|
||||
"确认删除",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning
|
||||
);
|
||||
|
||||
return result == MessageBoxResult.Yes;
|
||||
}
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **不要在构造函数中设置 Owner**:
|
||||
```csharp
|
||||
// ❌ 错误
|
||||
public MyDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
this.Owner = Application.Current.MainWindow; // 可能抛出异常
|
||||
}
|
||||
|
||||
// ✅ 正确:在外部设置
|
||||
var dialog = new MyDialog();
|
||||
DialogHelper.SetOwnerSafely(dialog);
|
||||
```
|
||||
|
||||
2. **非模态对话框也需要设置 Owner**:
|
||||
```csharp
|
||||
// 即使是 Show() 而非 ShowDialog(),也需要设置 Owner
|
||||
var dialog = new MyDialog();
|
||||
DialogHelper.SetOwnerSafely(dialog); // 确保正确置顶
|
||||
dialog.Show();
|
||||
```
|
||||
|
||||
3. **XAML 中不要设置 Owner**:
|
||||
```xml
|
||||
<!-- ❌ 错误 -->
|
||||
<Window ...
|
||||
Owner="{x:Static Application.Current.MainWindow}">
|
||||
|
||||
<!-- ✅ 正确:XAML中不设置,代码中设置 -->
|
||||
<Window ...>
|
||||
```
|
||||
134
.agents/skills/project-tools/utils/GeometryHelper.md
Normal file
134
.agents/skills/project-tools/utils/GeometryHelper.md
Normal file
@ -0,0 +1,134 @@
|
||||
# GeometryHelper 使用指南
|
||||
|
||||
## 文件位置
|
||||
|
||||
`src/Utils/GeometryHelper.cs`
|
||||
|
||||
## 用途
|
||||
|
||||
3D 几何计算工具类,提供距离、角度、向量等常用几何计算。**禁止自己实现几何计算**,必须复用此类。
|
||||
|
||||
## 核心方法
|
||||
|
||||
### 距离计算
|
||||
|
||||
```csharp
|
||||
// 两点间距离
|
||||
Point3D a = new Point3D(0, 0, 0);
|
||||
Point3D b = new Point3D(3, 4, 0);
|
||||
double distance = GeometryHelper.Distance(a, b); // 5.0
|
||||
|
||||
// 2D距离(忽略Z坐标)
|
||||
double distance2D = GeometryHelper.Distance2D(a, b);
|
||||
|
||||
// 距离的平方(性能优化,避免开方)
|
||||
double distanceSq = GeometryHelper.DistanceSquared(a, b);
|
||||
```
|
||||
|
||||
### 向量运算
|
||||
|
||||
```csharp
|
||||
// 向量减法(b - a)
|
||||
Vector3D vector = GeometryHelper.Subtract(b, a);
|
||||
|
||||
// 向量长度
|
||||
double length = GeometryHelper.VectorLength(vector);
|
||||
|
||||
// 单位向量
|
||||
Vector3D normalized = GeometryHelper.Normalize(vector);
|
||||
|
||||
// 点积
|
||||
double dot = GeometryHelper.DotProduct(v1, v2);
|
||||
|
||||
// 叉积
|
||||
Vector3D cross = GeometryHelper.CrossProduct(v1, v2);
|
||||
```
|
||||
|
||||
### 角度计算
|
||||
|
||||
```csharp
|
||||
// 向量夹角(弧度)
|
||||
double angle = GeometryHelper.AngleBetween(v1, v2);
|
||||
|
||||
// 向量夹角(角度)
|
||||
double angleDegrees = GeometryHelper.AngleBetweenDegrees(v1, v2);
|
||||
|
||||
// 三点形成的角度(以b为顶点)
|
||||
double angle = GeometryHelper.AngleAtVertex(a, b, c);
|
||||
```
|
||||
|
||||
### 点与线的关系
|
||||
|
||||
```csharp
|
||||
// 点到线段的最近点
|
||||
Point3D closestPoint = GeometryHelper.ProjectPointOnLineSegment(
|
||||
point, lineStart, lineEnd);
|
||||
|
||||
// 点到线段的距离
|
||||
double distance = GeometryHelper.DistancePointToLineSegment(
|
||||
point, lineStart, lineEnd);
|
||||
|
||||
// 点是否在线段上(含容差)
|
||||
bool isOnLine = GeometryHelper.IsPointOnLineSegment(
|
||||
point, lineStart, lineEnd, tolerance: 0.001);
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 示例1:计算路径总长度
|
||||
|
||||
```csharp
|
||||
// ❌ 错误:自己实现距离计算
|
||||
double dx = p2.X - p1.X;
|
||||
double dy = p2.Y - p1.Y;
|
||||
double dz = p2.Z - p1.Z;
|
||||
double distance = Math.Sqrt(dx*dx + dy*dy + dz*dz);
|
||||
|
||||
// ✅ 正确:使用 GeometryHelper
|
||||
double distance = GeometryHelper.Distance(p1, p2);
|
||||
```
|
||||
|
||||
### 示例2:检查三点是否共线
|
||||
|
||||
```csharp
|
||||
public bool AreCollinear(Point3D a, Point3D b, Point3D c, double tolerance = 0.001)
|
||||
{
|
||||
// 计算叉积,如果为0则共线
|
||||
Vector3D ab = GeometryHelper.Subtract(b, a);
|
||||
Vector3D ac = GeometryHelper.Subtract(c, a);
|
||||
Vector3D cross = GeometryHelper.CrossProduct(ab, ac);
|
||||
|
||||
return GeometryHelper.VectorLength(cross) < tolerance;
|
||||
}
|
||||
```
|
||||
|
||||
### 示例3:计算转弯角度
|
||||
|
||||
```csharp
|
||||
public double CalculateTurnAngle(Point3D prev, Point3D current, Point3D next)
|
||||
{
|
||||
// 入向量(从prev到current)
|
||||
Vector3D v1 = GeometryHelper.Subtract(current, prev);
|
||||
|
||||
// 出向量(从current到next)
|
||||
Vector3D v2 = GeometryHelper.Subtract(next, current);
|
||||
|
||||
// 计算夹角
|
||||
double angle = GeometryHelper.AngleBetweenDegrees(v1, v2);
|
||||
|
||||
return angle; // 0-180度
|
||||
}
|
||||
```
|
||||
|
||||
## 性能提示
|
||||
|
||||
```csharp
|
||||
// 大量距离比较时,使用距离的平方避免开方
|
||||
// ✅ 更快
|
||||
double distSq = GeometryHelper.DistanceSquared(a, b);
|
||||
if (distSq < threshold * threshold)
|
||||
|
||||
// ❌ 更慢(需要开方)
|
||||
double dist = GeometryHelper.Distance(a, b);
|
||||
if (dist < threshold)
|
||||
```
|
||||
144
.agents/skills/project-tools/utils/LogManager.md
Normal file
144
.agents/skills/project-tools/utils/LogManager.md
Normal file
@ -0,0 +1,144 @@
|
||||
# LogManager 使用指南
|
||||
|
||||
## 文件位置
|
||||
|
||||
`src/Utils/LogManager.cs`
|
||||
|
||||
## 用途
|
||||
|
||||
统一的日志记录管理器。在 Navisworks 插件环境中,`Console.WriteLine` 不可见,必须使用 LogManager。
|
||||
|
||||
## 日志级别
|
||||
|
||||
```csharp
|
||||
LogManager.Debug("调试信息"); // 开发调试使用
|
||||
LogManager.Info("普通信息"); // 一般操作记录
|
||||
LogManager.Warning("警告信息"); // 需要注意但非错误
|
||||
LogManager.Error("错误信息"); // 错误(可选异常参数)
|
||||
LogManager.Fatal("致命错误"); // 严重错误
|
||||
```
|
||||
|
||||
## 核心方法
|
||||
|
||||
### 基本信息日志
|
||||
|
||||
```csharp
|
||||
LogManager.Info("路径创建成功");
|
||||
LogManager.Info($"路径名称: {route.Name}, 长度: {route.TotalLength}");
|
||||
```
|
||||
|
||||
### 带异常的错误日志
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
// 某些操作
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// ✅ 正确:记录异常详情
|
||||
LogManager.Error($"操作失败: {ex.Message}", ex);
|
||||
}
|
||||
```
|
||||
|
||||
### 调试日志(开发时)
|
||||
|
||||
```csharp
|
||||
LogManager.Debug($"当前路径点数: {path.Points.Count}");
|
||||
LogManager.Debug($"网格大小: {grid.Width} x {grid.Height}");
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 示例1:记录操作开始和完成
|
||||
|
||||
```csharp
|
||||
public void CreatePath()
|
||||
{
|
||||
LogManager.Info("=== 开始创建路径 ===");
|
||||
|
||||
try
|
||||
{
|
||||
// 执行操作
|
||||
var route = GeneratePath();
|
||||
|
||||
LogManager.Info($"路径创建成功: {route.Name}, ID: {route.Id}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"路径创建失败: {ex.Message}", ex);
|
||||
throw; // 重新抛出或处理
|
||||
}
|
||||
|
||||
LogManager.Info("=== 创建路径完成 ===");
|
||||
}
|
||||
```
|
||||
|
||||
### 示例2:记录重要状态变更
|
||||
|
||||
```csharp
|
||||
public void SetStartPoint(Point3D point)
|
||||
{
|
||||
_startPoint = point;
|
||||
|
||||
LogManager.Info($"起点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})");
|
||||
|
||||
if (_endPoint != null)
|
||||
{
|
||||
double distance = GeometryHelper.Distance(point, _endPoint);
|
||||
LogManager.Info($"起点终点距离: {distance:F2} 米");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 日志文件位置
|
||||
|
||||
日志文件存储在:
|
||||
```
|
||||
C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\TransportPlugin\logs\
|
||||
```
|
||||
|
||||
文件命名:
|
||||
- `debug.log` - 当前日志
|
||||
- `debug.log.1`, `debug.log.2` - 历史日志(自动轮转)
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **永远不要使用 Console.WriteLine**:
|
||||
```csharp
|
||||
// ❌ 错误:在Navisworks中不可见
|
||||
Console.WriteLine("Error occurred");
|
||||
|
||||
// ✅ 正确:写入日志文件
|
||||
LogManager.Error("Error occurred");
|
||||
```
|
||||
|
||||
2. **错误日志要包含异常**:
|
||||
```csharp
|
||||
// ❌ 不够详细
|
||||
LogManager.Error($"失败: {ex.Message}");
|
||||
|
||||
// ✅ 包含堆栈信息
|
||||
LogManager.Error($"失败: {ex.Message}", ex);
|
||||
```
|
||||
|
||||
3. **避免在循环中大量记录**:
|
||||
```csharp
|
||||
// ❌ 可能导致日志文件过大
|
||||
foreach (var point in points)
|
||||
{
|
||||
LogManager.Debug($"处理点: {point.Name}"); // 1000个点=1000条日志
|
||||
}
|
||||
|
||||
// ✅ 只记录摘要
|
||||
LogManager.Debug($"开始处理 {points.Count} 个点...");
|
||||
// ... 处理 ...
|
||||
LogManager.Debug($"点处理完成");
|
||||
```
|
||||
|
||||
4. **使用类别前缀便于过滤**:
|
||||
```csharp
|
||||
LogManager.Info("[路径规划] 开始生成");
|
||||
LogManager.Info("[碰撞检测] 发现3个碰撞");
|
||||
LogManager.Info("[渲染] 路径显示完成");
|
||||
```
|
||||
126
.agents/skills/project-tools/utils/UnitsConverter.md
Normal file
126
.agents/skills/project-tools/utils/UnitsConverter.md
Normal file
@ -0,0 +1,126 @@
|
||||
# UnitsConverter 使用指南
|
||||
|
||||
## 文件位置
|
||||
|
||||
`src/Utils/UnitsConverter.cs`
|
||||
|
||||
## 用途
|
||||
|
||||
处理模型单位与米制单位之间的转换。Navisworks 文档可以使用不同的单位(米、毫米、英尺等),必须通过此工具类进行转换。
|
||||
|
||||
## 命名规范(极其重要)
|
||||
|
||||
| 单位类型 | 命名规则 | 示例 |
|
||||
|---------|---------|------|
|
||||
| **米单位** | 变量名必须以 `InMeters` 结尾 | `lengthInMeters`, `heightInMeters` |
|
||||
| **模型单位** | 变量名**无后缀** | `length`, `height`, `cellSize` |
|
||||
|
||||
```csharp
|
||||
// ✅ 正确
|
||||
public void SetSize(double lengthInMeters)
|
||||
{
|
||||
double factor = UnitsConverter.GetMetersToUnitsConversionFactor(...);
|
||||
double length = lengthInMeters * factor; // 模型单位,无后缀
|
||||
boundingBox = new BoundingBox3D(0, 0, 0, length, width, height);
|
||||
}
|
||||
|
||||
// ❌ 错误
|
||||
public void SetSize(double length) // 无法区分是米还是模型单位!
|
||||
{
|
||||
double scale = length / baseSize; // 单位不明确!
|
||||
}
|
||||
```
|
||||
|
||||
## 核心方法
|
||||
|
||||
### 获取转换因子
|
||||
|
||||
```csharp
|
||||
// 获取模型单位到米的转换因子(模型单位 × factor = 米)
|
||||
double factor = UnitsConverter.GetUnitsToMetersConversionFactor();
|
||||
|
||||
// 获取米到模型单位的转换因子(米 × factor = 模型单位)
|
||||
double factor = UnitsConverter.GetMetersToUnitsConversionFactor();
|
||||
```
|
||||
|
||||
### 直接转换值
|
||||
|
||||
```csharp
|
||||
// 模型单位 → 米
|
||||
double meters = UnitsConverter.ConvertToMeters(distanceInModelUnits);
|
||||
|
||||
// 米 → 模型单位
|
||||
double modelUnits = UnitsConverter.ConvertFromMeters(distanceInMeters);
|
||||
```
|
||||
|
||||
### 带单位的转换(从文档)
|
||||
|
||||
```csharp
|
||||
// 从当前文档获取单位信息
|
||||
Units units = Application.ActiveDocument.Units;
|
||||
|
||||
// 使用特定单位进行转换
|
||||
double meters = UnitsConverter.ConvertToMeters(distance, units);
|
||||
double modelUnits = UnitsConverter.ConvertFromMeters(distanceInMeters, units);
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 示例1:读取配置并转换为模型单位
|
||||
|
||||
```csharp
|
||||
// 配置中存储的是米
|
||||
double cellSizeInMeters = ConfigManager.Instance.Current.PathEditing.CellSizeMeters;
|
||||
|
||||
// 转换为模型单位用于计算
|
||||
double cellSize = UnitsConverter.ConvertFromMeters(cellSizeInMeters);
|
||||
```
|
||||
|
||||
### 示例2:显示长度给用户
|
||||
|
||||
```csharp
|
||||
// 路径长度是模型单位
|
||||
double totalLength = route.TotalLength;
|
||||
|
||||
// 转换为米显示
|
||||
double lengthInMeters = UnitsConverter.ConvertToMeters(totalLength);
|
||||
string displayText = $"总长度: {lengthInMeters:F2} 米";
|
||||
```
|
||||
|
||||
### 示例3:完整的函数参数处理
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// 生成网格地图
|
||||
/// </summary>
|
||||
/// <param name="cellSizeInMeters">网格单元大小(米)</param>
|
||||
public GridMap Generate(double cellSizeInMeters)
|
||||
{
|
||||
// 转换为模型单位
|
||||
double cellSize = UnitsConverter.ConvertFromMeters(cellSizeInMeters);
|
||||
|
||||
// 后续计算使用 cellSize(模型单位)
|
||||
int gridX = (int)(bounds.Width / cellSize);
|
||||
int gridY = (int)(bounds.Height / cellSize);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 常见错误
|
||||
|
||||
```csharp
|
||||
// ❌ 错误1:硬编码转换因子
|
||||
private const double MM_TO_METERS = 0.001; // 危险!不同文档单位不同
|
||||
double meters = modelValue * MM_TO_METERS; // 只在毫米文档正确
|
||||
|
||||
// ❌ 错误2:混淆命名
|
||||
double cellSize = 0.5; // 这是米还是模型单位?
|
||||
if (height > cellSize) // 单位不匹配的严重bug!
|
||||
|
||||
// ❌ 错误3:混用不同文档的单位
|
||||
Units doc1Units = doc1.Units;
|
||||
Units doc2Units = doc2.Units;
|
||||
double value1 = ConvertToMeters(modelValue, doc1Units);
|
||||
double value2 = ConvertToMeters(modelValue, doc2Units); // 可能不同!
|
||||
```
|
||||
184
CHANGELOG.md
184
CHANGELOG.md
@ -1778,6 +1778,98 @@ var stats = gridMap.GetHeightCalculationStats();
|
||||
|
||||
---
|
||||
|
||||
## [0.1.4] - 2025-06-18
|
||||
|
||||
### 修复
|
||||
|
||||
- **点击监听问题**: 修复鼠标点击没有效果的问题
|
||||
- 添加了点击状态跟踪机制,避免重复处理同一选择
|
||||
- 增加最小点击间隔(500ms)防止误触
|
||||
- 优化定时器间隔从100ms增加到200ms,减少资源消耗
|
||||
- 在处理完点击后自动清除选择,为下次点击做准备
|
||||
- **通道高亮显示**: 修复通道自动高亮显示丢失的问题
|
||||
- 在进入路径编辑模式前先清除之前的高亮
|
||||
- 改用更鲜艳的LimeGreen颜色提高可见性
|
||||
- 添加详细的高亮状态日志和错误处理
|
||||
- 增强高亮结果验证机制
|
||||
- **重复路径点**: 修复点击3次却产生81条记录的问题
|
||||
- 实现选择去重机制,相同选择在时间间隔内不重复处理
|
||||
- 添加ModelItem对比逻辑
|
||||
- 优化定时器触发频率
|
||||
- **点选详情显示**: 实现路径点列表中点选时的详情显示
|
||||
- 在状态栏显示选中点的完整信息(名称、类型、坐标、创建时间)
|
||||
- 修复PointsListBox_SelectedIndexChanged事件处理
|
||||
- 添加点选择日志记录
|
||||
- 实现地图中选中点的高亮重绘
|
||||
|
||||
### 技术改进
|
||||
|
||||
- **状态管理**: 增强点击状态跟踪和管理
|
||||
- 添加_lastProcessedItem和_lastProcessedTime字段
|
||||
- 实现MinClickInterval常量控制最小点击间隔
|
||||
- **日志系统**: 改进调试日志的详细程度
|
||||
- 点击监听过程的详细日志
|
||||
- 通道高亮过程的状态跟踪
|
||||
- 异常堆栈信息记录
|
||||
- **用户体验**: 优化交互反馈
|
||||
- 改进状态消息显示选中通道数量
|
||||
- 在状态栏显示详细的点信息
|
||||
- 清除无效选择的自动处理
|
||||
|
||||
## 版本 [0.1.3] - 2025-06-18
|
||||
|
||||
### 修复
|
||||
|
||||
- **兼容性修复**: 修复C# 7.3兼容性问题,移除了nullable引用类型语法
|
||||
- **API兼容性**: 修复Navisworks 2017 API兼容性问题
|
||||
- 替换了不存在的`View.RequestSavedViewpoint()`方法
|
||||
- 使用`Model.RootItem.BoundingBox()`替代不存在的`Model.BoundingBox()`
|
||||
- 实现了相机位置估算算法作为API限制的替代方案
|
||||
- **方法签名优化**: 将nullable返回类型改为out参数模式以提高兼容性
|
||||
- `GetPreciseClickPoint()` 方法
|
||||
- `GetRaycastClickPoint()` 方法
|
||||
- `GetSurfacePointFromGeometry()` 方法
|
||||
- `GetCameraPosition()` 方法
|
||||
- **射线投射算法**: 完善了射线与包围盒交点计算的错误处理
|
||||
|
||||
### 技术改进
|
||||
|
||||
- **错误处理**: 增强了所有射线投射相关方法的错误处理和日志记录
|
||||
- **后备方案**: 在精确坐标获取失败时提供包围盒中心点作为后备方案
|
||||
- **日志优化**: 改进了调试日志的详细程度和格式
|
||||
|
||||
## 技术债务和改进计划
|
||||
|
||||
### 当前已知问题
|
||||
|
||||
- 暂无已知的功能性问题
|
||||
|
||||
### 已完成功能 ✅
|
||||
|
||||
1. ✅ **TimeLiner 集成**:已实现完整的 TimeLiner 集成功能(v0.1.9)
|
||||
- 运输任务自动创建和管理
|
||||
- 动画状态实时同步
|
||||
- 完善的错误处理和降级机制
|
||||
|
||||
### 下一步开发计划
|
||||
|
||||
1. **路径规划功能**:实现 A* 算法的 3D 路径规划
|
||||
2. **动态碰撞检测增强**:进一步集成 Clash Detective 功能
|
||||
3. **动画和可视化优化**:添加更多路径动画效果和碰撞高亮显示
|
||||
4. **DELMIA导出**:实现结构化数据导出功能
|
||||
5. **TimeLiner 高级功能**:
|
||||
- 支持任务时间线编辑
|
||||
- 批量任务管理
|
||||
- 任务导入导出功能
|
||||
|
||||
### 性能优化目标
|
||||
|
||||
- 大型模型的处理性能优化
|
||||
- 异步操作的用户界面响应性改进
|
||||
- 内存使用优化和资源清理机制完善
|
||||
|
||||
---
|
||||
|
||||
## 版本 [0.1.2] - 2025-06-17
|
||||
|
||||
### 增加分层可见性控制功能
|
||||
@ -1844,95 +1936,3 @@ var stats = gridMap.GetHeightCalculationStats();
|
||||
- ✅ 8种物流元素类型的按钮功能正常,属性设置准确
|
||||
- ✅ COM API调用稳定,无内存泄漏或崩溃问题
|
||||
- ✅ 编译通过,插件在Navisworks 2017中正常加载和运行
|
||||
|
||||
---
|
||||
|
||||
## 版本 [0.1.3] - 2024-01-XX
|
||||
|
||||
### 修复
|
||||
|
||||
- **兼容性修复**: 修复C# 7.3兼容性问题,移除了nullable引用类型语法
|
||||
- **API兼容性**: 修复Navisworks 2017 API兼容性问题
|
||||
- 替换了不存在的`View.RequestSavedViewpoint()`方法
|
||||
- 使用`Model.RootItem.BoundingBox()`替代不存在的`Model.BoundingBox()`
|
||||
- 实现了相机位置估算算法作为API限制的替代方案
|
||||
- **方法签名优化**: 将nullable返回类型改为out参数模式以提高兼容性
|
||||
- `GetPreciseClickPoint()` 方法
|
||||
- `GetRaycastClickPoint()` 方法
|
||||
- `GetSurfacePointFromGeometry()` 方法
|
||||
- `GetCameraPosition()` 方法
|
||||
- **射线投射算法**: 完善了射线与包围盒交点计算的错误处理
|
||||
|
||||
### 技术改进
|
||||
|
||||
- **错误处理**: 增强了所有射线投射相关方法的错误处理和日志记录
|
||||
- **后备方案**: 在精确坐标获取失败时提供包围盒中心点作为后备方案
|
||||
- **日志优化**: 改进了调试日志的详细程度和格式
|
||||
|
||||
## 技术债务和改进计划
|
||||
|
||||
### 当前已知问题
|
||||
|
||||
- 暂无已知的功能性问题
|
||||
|
||||
### 已完成功能 ✅
|
||||
|
||||
1. ✅ **TimeLiner 集成**:已实现完整的 TimeLiner 集成功能(v0.1.9)
|
||||
- 运输任务自动创建和管理
|
||||
- 动画状态实时同步
|
||||
- 完善的错误处理和降级机制
|
||||
|
||||
### 下一步开发计划
|
||||
|
||||
1. **路径规划功能**:实现 A* 算法的 3D 路径规划
|
||||
2. **动态碰撞检测增强**:进一步集成 Clash Detective 功能
|
||||
3. **动画和可视化优化**:添加更多路径动画效果和碰撞高亮显示
|
||||
4. **DELMIA导出**:实现结构化数据导出功能
|
||||
5. **TimeLiner 高级功能**:
|
||||
- 支持任务时间线编辑
|
||||
- 批量任务管理
|
||||
- 任务导入导出功能
|
||||
|
||||
### 性能优化目标
|
||||
|
||||
- 大型模型的处理性能优化
|
||||
- 异步操作的用户界面响应性改进
|
||||
- 内存使用优化和资源清理机制完善
|
||||
|
||||
## [0.1.4] - 2024-01-XX
|
||||
|
||||
### 修复
|
||||
|
||||
- **点击监听问题**: 修复鼠标点击没有效果的问题
|
||||
- 添加了点击状态跟踪机制,避免重复处理同一选择
|
||||
- 增加最小点击间隔(500ms)防止误触
|
||||
- 优化定时器间隔从100ms增加到200ms,减少资源消耗
|
||||
- 在处理完点击后自动清除选择,为下次点击做准备
|
||||
- **通道高亮显示**: 修复通道自动高亮显示丢失的问题
|
||||
- 在进入路径编辑模式前先清除之前的高亮
|
||||
- 改用更鲜艳的LimeGreen颜色提高可见性
|
||||
- 添加详细的高亮状态日志和错误处理
|
||||
- 增强高亮结果验证机制
|
||||
- **重复路径点**: 修复点击3次却产生81条记录的问题
|
||||
- 实现选择去重机制,相同选择在时间间隔内不重复处理
|
||||
- 添加ModelItem对比逻辑
|
||||
- 优化定时器触发频率
|
||||
- **点选详情显示**: 实现路径点列表中点选时的详情显示
|
||||
- 在状态栏显示选中点的完整信息(名称、类型、坐标、创建时间)
|
||||
- 修复PointsListBox_SelectedIndexChanged事件处理
|
||||
- 添加点选择日志记录
|
||||
- 实现地图中选中点的高亮重绘
|
||||
|
||||
### 技术改进
|
||||
|
||||
- **状态管理**: 增强点击状态跟踪和管理
|
||||
- 添加_lastProcessedItem和_lastProcessedTime字段
|
||||
- 实现MinClickInterval常量控制最小点击间隔
|
||||
- **日志系统**: 改进调试日志的详细程度
|
||||
- 点击监听过程的详细日志
|
||||
- 通道高亮过程的状态跟踪
|
||||
- 异常堆栈信息记录
|
||||
- **用户体验**: 优化交互反馈
|
||||
- 改进状态消息显示选中通道数量
|
||||
- 在状态栏显示详细的点信息
|
||||
- 清除无效选择的自动处理
|
||||
|
||||
@ -205,6 +205,7 @@
|
||||
<Compile Include="src\PathPlanning\VoxelGridVisualizer.cs" />
|
||||
<Compile Include="src\PathPlanning\RailGeometryHelper.cs" />
|
||||
<Compile Include="src\PathPlanning\AerialPathGenerator.cs" />
|
||||
<Compile Include="src\Commands\CreateAerialPathCommand.cs" />
|
||||
<!-- UI - WPF -->
|
||||
<Compile Include="src\UI\WPF\Views\LogisticsControlPanel.xaml.cs">
|
||||
<DependentUpon>LogisticsControlPanel.xaml</DependentUpon>
|
||||
@ -295,6 +296,7 @@
|
||||
<!-- UI - WPF Commands and Models -->
|
||||
<Compile Include="src\UI\WPF\Commands\RelayCommand.cs" />
|
||||
<Compile Include="src\UI\WPF\Commands\LayerManagementCommands.cs" />
|
||||
<Compile Include="src\UI\WPF\Converters\HoistingConverters.cs" />
|
||||
<Compile Include="src\UI\WPF\Converters\BoolToVisibilityConverter.cs" />
|
||||
<Compile Include="src\UI\WPF\Converters\BoolToBrushConverter.cs" />
|
||||
<Compile Include="src\UI\WPF\Converters\BoolToOpacityConverter.cs" />
|
||||
@ -306,6 +308,7 @@
|
||||
<Compile Include="src\UI\WPF\Converters\NullToDashConverter.cs" />
|
||||
<Compile Include="src\UI\WPF\Converters\PathToImageConverter.cs" />
|
||||
<Compile Include="src\UI\WPF\Models\LogisticsModel.cs" />
|
||||
<Compile Include="src\UI\WPF\Models\HoistingLevelItem.cs" />
|
||||
<Compile Include="src\UI\WPF\Models\PathRouteViewModel.cs" />
|
||||
<Compile Include="src\UI\WPF\Models\SplitPreviewItem.cs" />
|
||||
<!-- Utils -->
|
||||
|
||||
@ -2,6 +2,11 @@
|
||||
|
||||
## 功能点
|
||||
|
||||
### [2026/2/24]
|
||||
|
||||
1. [x] (优化)吊装路径支持
|
||||
2. [x] (功能)
|
||||
|
||||
### [2026/2/14]
|
||||
|
||||
1. [x] (优化)实现完整的路径分析功能
|
||||
|
||||
188
doc/working/multi_level_hoisting_path.md
Normal file
188
doc/working/multi_level_hoisting_path.md
Normal file
@ -0,0 +1,188 @@
|
||||
# 多层阶梯式吊装路径开发方案
|
||||
|
||||
## 概述
|
||||
|
||||
本文档描述多层阶梯式吊装路径(Multi-Level Stepped Hoisting Path)的实现方案。该功能支持在多个水平高度进行正交移动,适用于车间桁车等复杂吊装场景。
|
||||
|
||||
### 术语定义
|
||||
|
||||
- **单层吊装路径**:传统的"起吊 → 平移 → 下降"三段式路径
|
||||
- **多层阶梯式吊装路径**:支持在多个高度层进行"提升/下降 → 平移"的阶梯式路径
|
||||
- **正交路径**:运动严格按 X/Y/Z 轴方向,每次只改变一个维度
|
||||
|
||||
## 需求规格
|
||||
|
||||
### 功能需求
|
||||
|
||||
1. 支持在多个高度层进行水平移动
|
||||
2. 支持上升和下降过程(可混合)
|
||||
3. 每个层级可设置不同的水平目标位置
|
||||
4. 保持与现有单层吊装路径的向后兼容
|
||||
|
||||
### 运动轨迹示例
|
||||
|
||||
```
|
||||
单层吊装:
|
||||
地面起吊 → 提升高度H → 水平移动到终点 → 下降落地
|
||||
↑______________↓
|
||||
|
||||
多层吊装(2层提升+1层下降):
|
||||
地面起吊 → 提升H1 → 水平移动到P1 →
|
||||
提升H2 → 水平移动到P2 →
|
||||
下降H3 → 水平移动到P3 →
|
||||
下降落地
|
||||
↑______↑______↓______↓
|
||||
```
|
||||
|
||||
## 架构设计
|
||||
|
||||
### 数据模型
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ AerialPathParameters │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ PathType: PathType.Hoisting │
|
||||
│ StartPoint: Point3D ← 起吊点(地面) │
|
||||
│ EndPoint: Point3D ← 落点(地面) │
|
||||
│ LiftHeightMeters: double ← 单层模式使用(保留) │
|
||||
│ Mode: HoistingMode ← SingleLevel/MultiLevel │
|
||||
│ Levels: List<HoistingLevel> ← 多层模式层级列表 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ HoistingLevel │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Index: int ← 层级序号 │
|
||||
│ HeightInMeters: double ← 相对起吊点的高度 │
|
||||
│ HorizontalTarget: Point3D ← 水平目标位置(XY) │
|
||||
│ IsRise: bool ← true=提升, false=下降 │
|
||||
│ Description: string ← 层级描述 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 兼容性设计
|
||||
|
||||
| 组件 | 兼容性策略 |
|
||||
|------|-----------|
|
||||
| `PathRoute.Points` | `List<PathPoint>` 天然支持可变数量,无需修改 |
|
||||
| `PathType.Hoisting` | 继续使用现有枚举值,无需新增 |
|
||||
| XML序列化 | 按属性序列化,点数不影响格式 |
|
||||
| 数据库存储 | 路径点表结构不变,仅插入行数变化 |
|
||||
| `AerialHeightDialog` | 保留现有对话框,单层模式继续使用 |
|
||||
|
||||
## 实现计划
|
||||
|
||||
### 阶段1:核心数据结构
|
||||
|
||||
**目标**:创建数据模型,扩展参数类,实现多层路径生成器
|
||||
|
||||
**涉及文件**:
|
||||
1. `src/Core/PathPlanningModels.cs` - 添加 `HoistingLevel` 类
|
||||
2. `src/Commands/CreateAerialPathCommand.cs` - 扩展参数和模式枚举
|
||||
3. `src/PathPlanning/AerialPathGenerator.cs` - 添加多层生成方法
|
||||
|
||||
**实现步骤**:
|
||||
1. 在 `PathPlanningModels.cs` 中添加 `HoistingLevel` 类和 `HoistingMode` 枚举
|
||||
2. 在 `CreateAerialPathCommand.cs` 中扩展 `AerialPathParameters`
|
||||
3. 在 `AerialPathGenerator.cs` 中实现 `GenerateMultiLevelHoistingPath` 方法
|
||||
4. 修改 `CreateAerialPathCommand.ExecuteInternalAsync` 支持两种模式
|
||||
|
||||
**向后兼容保证**:
|
||||
- 现有 `GenerateThreeStepHoistingPath` 方法保持不变
|
||||
- `AerialPathParameters.LiftHeightMeters` 继续存在
|
||||
- 新增 `Mode` 属性默认值为 `SingleLevel`
|
||||
|
||||
### 阶段2:UI对话框
|
||||
|
||||
**目标**:创建多层吊装路径输入对话框
|
||||
|
||||
**涉及文件**:
|
||||
1. `src/UI/WPF/Views/MultiLevelHoistingDialog.xaml` - 新建
|
||||
2. `src/UI/WPF/Views/MultiLevelHoistingDialog.xaml.cs` - 新建
|
||||
|
||||
**功能需求**:
|
||||
- 显示层级列表(可添加/删除)
|
||||
- 每个层级设置:高度、方向(提升/下降)、描述
|
||||
- 水平目标位置通过3D点击获取
|
||||
- 实时预览路径点数
|
||||
|
||||
### 阶段3:ViewModel集成
|
||||
|
||||
**目标**:在 PathEditingViewModel 中集成多层模式
|
||||
|
||||
**涉及文件**:
|
||||
1. `src/UI/WPF/ViewModels/PathEditingViewModel.cs`
|
||||
|
||||
**功能需求**:
|
||||
- 添加模式选择命令
|
||||
- 集成多层对话框
|
||||
- 支持在创建流程中动态添加层级
|
||||
|
||||
## API规范
|
||||
|
||||
### HoistingLevel 类
|
||||
|
||||
```csharp
|
||||
public class HoistingLevel
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public double HeightInMeters { get; set; }
|
||||
public Point3D HorizontalTarget { get; set; }
|
||||
public bool IsRise { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
### HoistingMode 枚举
|
||||
|
||||
```csharp
|
||||
public enum HoistingMode
|
||||
{
|
||||
SingleLevel, // 单层(默认,向后兼容)
|
||||
MultiLevel // 多层阶梯式
|
||||
}
|
||||
```
|
||||
|
||||
### AerialPathParameters 扩展
|
||||
|
||||
```csharp
|
||||
public class AerialPathParameters
|
||||
{
|
||||
// 现有字段保留...
|
||||
public HoistingMode Mode { get; set; } = HoistingMode.SingleLevel;
|
||||
public List<HoistingLevel> Levels { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
### AerialPathGenerator 新方法
|
||||
|
||||
```csharp
|
||||
public List<PathPoint> GenerateMultiLevelHoistingPath(
|
||||
Point3D startPoint,
|
||||
Point3D endPoint,
|
||||
List<HoistingLevel> levels)
|
||||
```
|
||||
|
||||
## 测试要点
|
||||
|
||||
1. **向后兼容测试**:单层模式路径生成与之前完全一致
|
||||
2. **多层路径测试**:2层、3层、混合升降路径生成正确
|
||||
3. **数据序列化测试**:多层路径保存/加载正确
|
||||
4. **渲染测试**:多层路径在3D视图中正确显示
|
||||
5. **动画测试**:动画沿多层路径正确插值
|
||||
|
||||
## 风险分析
|
||||
|
||||
| 风险 | 可能性 | 影响 | 缓解措施 |
|
||||
|------|-------|------|---------|
|
||||
| 现有单层功能被破坏 | 低 | 高 | 保留原方法,添加新实现,充分测试 |
|
||||
| 路径点过多影响性能 | 中 | 中 | 限制最大层级数(如10层) |
|
||||
| 数据库兼容性问题 | 低 | 高 | 验证现有表结构支持可变点数 |
|
||||
|
||||
## 版本记录
|
||||
|
||||
| 版本 | 日期 | 描述 |
|
||||
|------|------|------|
|
||||
| 1.0 | 2026-02-24 | 初始方案 |
|
||||
@ -49,14 +49,35 @@ namespace NavisworksTransport.Commands
|
||||
|
||||
if (_parameters.PathType == PathType.Hoisting)
|
||||
{
|
||||
// 吊装:使用 AerialPathGenerator 生成4个路径点
|
||||
var generator = new AerialPathGenerator();
|
||||
pathPoints = generator.GenerateThreeStepHoistingPath(
|
||||
_parameters.StartPoint,
|
||||
_parameters.EndPoint,
|
||||
_parameters.LiftHeightMeters);
|
||||
|
||||
LogManager.Info($"吊装路径: 起点=({_parameters.StartPoint.X:F2},{_parameters.StartPoint.Y:F2},{_parameters.StartPoint.Z:F2}), 终点=({_parameters.EndPoint.X:F2},{_parameters.EndPoint.Y:F2},{_parameters.EndPoint.Z:F2}), 提升高度={_parameters.LiftHeightMeters:F2}米");
|
||||
if (_parameters.HoistingMode == HoistingMode.MultiLevel &&
|
||||
_parameters.HoistingLevels != null &&
|
||||
_parameters.HoistingLevels.Count > 0)
|
||||
{
|
||||
// 多层阶梯式吊装
|
||||
pathPoints = generator.GenerateMultiLevelHoistingPath(
|
||||
_parameters.StartPoint,
|
||||
_parameters.EndPoint,
|
||||
_parameters.HoistingLevels);
|
||||
|
||||
LogManager.Info($"多层吊装路径: 起点=({_parameters.StartPoint.X:F2},{_parameters.StartPoint.Y:F2},{_parameters.StartPoint.Z:F2}), " +
|
||||
$"终点=({_parameters.EndPoint.X:F2},{_parameters.EndPoint.Y:F2},{_parameters.EndPoint.Z:F2}), " +
|
||||
$"层级数={_parameters.HoistingLevels.Count}, 总点数={pathPoints.Count}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 单层吊装(传统模式,默认)
|
||||
double liftHeightModelUnits = UnitsConverter.ConvertFromMeters(_parameters.LiftHeightMeters);
|
||||
pathPoints = generator.GenerateThreeStepHoistingPath(
|
||||
_parameters.StartPoint,
|
||||
_parameters.EndPoint,
|
||||
liftHeightModelUnits);
|
||||
|
||||
LogManager.Info($"单层吊装路径: 起点=({_parameters.StartPoint.X:F2},{_parameters.StartPoint.Y:F2},{_parameters.StartPoint.Z:F2}), " +
|
||||
$"终点=({_parameters.EndPoint.X:F2},{_parameters.EndPoint.Y:F2},{_parameters.EndPoint.Z:F2}), " +
|
||||
$"提升高度={_parameters.LiftHeightMeters:F2}米");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -119,8 +140,25 @@ namespace NavisworksTransport.Commands
|
||||
if (_parameters.EndPoint == null)
|
||||
return PathPlanningResult.ValidationFailure("终点不能为空");
|
||||
|
||||
if (_parameters.LiftHeightMeters <= 0)
|
||||
return PathPlanningResult.ValidationFailure("提升高度必须大于0");
|
||||
// 单层模式:检查 LiftHeightMeters
|
||||
// 多层模式:检查 HoistingLevels
|
||||
if (_parameters.HoistingMode == HoistingMode.SingleLevel)
|
||||
{
|
||||
if (_parameters.LiftHeightMeters <= 0)
|
||||
return PathPlanningResult.ValidationFailure("提升高度必须大于0");
|
||||
}
|
||||
else if (_parameters.HoistingMode == HoistingMode.MultiLevel)
|
||||
{
|
||||
if (_parameters.HoistingLevels == null || _parameters.HoistingLevels.Count == 0)
|
||||
return PathPlanningResult.ValidationFailure("请至少设置一个吊装层级");
|
||||
|
||||
// 检查每个层级的高度
|
||||
foreach (var level in _parameters.HoistingLevels)
|
||||
{
|
||||
if (level.HeightInMeters <= 0)
|
||||
return PathPlanningResult.ValidationFailure($"层级{level.Index + 1}的高度必须大于0");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_parameters.PathType == PathType.Rail)
|
||||
{
|
||||
@ -180,6 +218,7 @@ namespace NavisworksTransport.Commands
|
||||
|
||||
/// <summary>
|
||||
/// 吊装参数:提升高度(米)
|
||||
/// 单层吊装模式使用,多层模式下该值被忽略
|
||||
/// </summary>
|
||||
public double LiftHeightMeters { get; set; }
|
||||
|
||||
@ -187,5 +226,25 @@ namespace NavisworksTransport.Commands
|
||||
/// 空轨参数:点击点列表
|
||||
/// </summary>
|
||||
public List<Point3D> ClickedPoints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 吊装路径模式(默认单层,向后兼容)
|
||||
/// </summary>
|
||||
public HoistingMode HoistingMode { get; set; } = HoistingMode.SingleLevel;
|
||||
|
||||
/// <summary>
|
||||
/// 多层吊装:层级信息列表(仅多层模式使用)
|
||||
/// </summary>
|
||||
public List<HoistingLevel> HoistingLevels { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认构造函数
|
||||
/// </summary>
|
||||
public AerialPathParameters()
|
||||
{
|
||||
HoistingMode = HoistingMode.SingleLevel;
|
||||
HoistingLevels = new List<HoistingLevel>();
|
||||
ClickedPoints = new List<Point3D>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3980,8 +3980,10 @@ namespace NavisworksTransport
|
||||
// 2. 重置为无活动工具状态
|
||||
try
|
||||
{
|
||||
// 使用Tool.None重置到无活动工具状态,让Navisworks处理默认导航
|
||||
Application.MainDocument.Tool.Value = Tool.None;
|
||||
// 使用Tool.Select切换到选择工具,确保完全退出ToolPlugin模式
|
||||
// Tool.None可能不足以释放ToolPlugin对键盘输入的拦截
|
||||
Application.MainDocument.Tool.Value = Tool.Select;
|
||||
LogManager.Debug("[ToolPlugin] 已重置工具状态为Select");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@ -2530,4 +2530,101 @@ namespace NavisworksTransport
|
||||
public string Version { get; set; } = "1.0";
|
||||
public string Description { get; set; } = "";
|
||||
}
|
||||
|
||||
#region 多层阶梯式吊装路径数据模型
|
||||
|
||||
/// <summary>
|
||||
/// 吊装路径模式枚举
|
||||
/// </summary>
|
||||
public enum HoistingMode
|
||||
{
|
||||
/// <summary>
|
||||
/// 单层吊装(传统模式,向后兼容)
|
||||
/// </summary>
|
||||
SingleLevel,
|
||||
|
||||
/// <summary>
|
||||
/// 多层阶梯式吊装
|
||||
/// </summary>
|
||||
MultiLevel
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 吊装层级信息 - 表示多层吊装中的一个水平移动层级
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class HoistingLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// 层级序号(从0开始)
|
||||
/// </summary>
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 该层级的高度(相对于起吊点的垂直高度,米)
|
||||
/// </summary>
|
||||
public double HeightInMeters { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 该层级的水平移动目标点(XY位置,Z坐标会被忽略)
|
||||
/// </summary>
|
||||
public Point3D HorizontalTarget { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 运动方向:true=从上一位置上升到此高度,false=从上一位置下降到此高度
|
||||
/// </summary>
|
||||
public bool IsRise { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 层级描述(如"桁车移动层"、"避让层"等)
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认构造函数
|
||||
/// </summary>
|
||||
public HoistingLevel()
|
||||
{
|
||||
Index = 0;
|
||||
HeightInMeters = 0.0;
|
||||
HorizontalTarget = new Point3D();
|
||||
IsRise = true;
|
||||
Description = string.Empty;
|
||||
CreatedTime = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 带参数构造函数
|
||||
/// </summary>
|
||||
/// <param name="index">层级序号</param>
|
||||
/// <param name="heightInMeters">高度(米)</param>
|
||||
/// <param name="horizontalTarget">水平目标点</param>
|
||||
/// <param name="isRise">是否为上升</param>
|
||||
/// <param name="description">描述</param>
|
||||
public HoistingLevel(int index, double heightInMeters, Point3D horizontalTarget, bool isRise = true, string description = "")
|
||||
{
|
||||
Index = index;
|
||||
HeightInMeters = heightInMeters;
|
||||
HorizontalTarget = horizontalTarget ?? new Point3D();
|
||||
IsRise = isRise;
|
||||
Description = description ?? string.Empty;
|
||||
CreatedTime = DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回层级描述字符串
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
string direction = IsRise ? "上升" : "下降";
|
||||
return $"层级{Index + 1}: {direction}到{HeightInMeters:F2}米,目标({HorizontalTarget.X:F2}, {HorizontalTarget.Y:F2}) {(string.IsNullOrEmpty(Description) ? "" : $"- {Description}")}";
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@ -221,5 +221,99 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
return pathPoints;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成多层阶梯式吊装路径
|
||||
/// 支持在多个高度层进行水平移动,可混合上升和下降
|
||||
/// </summary>
|
||||
/// <param name="startPoint">起点(地面起吊位置)</param>
|
||||
/// <param name="endPoint">终点(地面落点位置)</param>
|
||||
/// <param name="levels">层级信息列表(按顺序执行)</param>
|
||||
/// <returns>路径点列表</returns>
|
||||
public List<PathPoint> GenerateMultiLevelHoistingPath(
|
||||
Point3D startPoint,
|
||||
Point3D endPoint,
|
||||
List<HoistingLevel> levels)
|
||||
{
|
||||
if (startPoint == null)
|
||||
throw new ArgumentNullException(nameof(startPoint));
|
||||
if (endPoint == null)
|
||||
throw new ArgumentNullException(nameof(endPoint));
|
||||
if (levels == null || levels.Count == 0)
|
||||
throw new ArgumentException("至少需要指定一个层级", nameof(levels));
|
||||
|
||||
var pathPoints = new List<PathPoint>();
|
||||
|
||||
// 点1:起吊点(物体初始位置)
|
||||
var startPointPath = new PathPoint(
|
||||
startPoint,
|
||||
"起吊点",
|
||||
PathPointType.StartPoint);
|
||||
startPointPath.Direction = HoistingPointDirection.Vertical;
|
||||
pathPoints.Add(startPointPath);
|
||||
|
||||
Point3D currentPoint = startPoint;
|
||||
|
||||
// 按层级顺序生成路径点
|
||||
for (int i = 0; i < levels.Count; i++)
|
||||
{
|
||||
var level = levels[i];
|
||||
double levelHeightModelUnits = UnitsConverter.ConvertFromMeters(level.HeightInMeters);
|
||||
double currentHeight = startPoint.Z + levelHeightModelUnits;
|
||||
|
||||
// 垂直移动点(上升或下降到该层级高度)
|
||||
var verticalPoint = new Point3D(
|
||||
currentPoint.X,
|
||||
currentPoint.Y,
|
||||
currentHeight);
|
||||
|
||||
string verticalPointName = level.IsRise ? $"提升点{i + 1}" : $"下降点{i + 1}";
|
||||
var verticalPathPoint = new PathPoint(
|
||||
verticalPoint,
|
||||
verticalPointName,
|
||||
PathPointType.WayPoint);
|
||||
verticalPathPoint.Direction = HoistingPointDirection.Vertical;
|
||||
pathPoints.Add(verticalPathPoint);
|
||||
|
||||
// 水平移动点(移动到该层级的目标位置,保持高度)
|
||||
var horizontalPoint = new Point3D(
|
||||
level.HorizontalTarget.X,
|
||||
level.HorizontalTarget.Y,
|
||||
currentHeight);
|
||||
|
||||
string horizontalPointName = $"平移点{i + 1}";
|
||||
var horizontalPathPoint = new PathPoint(
|
||||
horizontalPoint,
|
||||
horizontalPointName,
|
||||
PathPointType.WayPoint);
|
||||
horizontalPathPoint.Direction = HoistingPointDirection.Longitudinal;
|
||||
pathPoints.Add(horizontalPathPoint);
|
||||
|
||||
// 更新当前点为水平移动后的位置
|
||||
currentPoint = horizontalPoint;
|
||||
}
|
||||
|
||||
// 最后:垂直下降到地面终点
|
||||
var descendPoint = new Point3D(
|
||||
currentPoint.X,
|
||||
currentPoint.Y,
|
||||
endPoint.Z);
|
||||
var descendPathPoint = new PathPoint(
|
||||
descendPoint,
|
||||
"最终下降点",
|
||||
PathPointType.WayPoint);
|
||||
descendPathPoint.Direction = HoistingPointDirection.Vertical;
|
||||
pathPoints.Add(descendPathPoint);
|
||||
|
||||
// 点N:落地点(物体最终位置)
|
||||
var endPointPath = new PathPoint(
|
||||
endPoint,
|
||||
"落地点",
|
||||
PathPointType.EndPoint);
|
||||
endPointPath.Direction = HoistingPointDirection.Vertical;
|
||||
pathPoints.Add(endPointPath);
|
||||
|
||||
return pathPoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
81
src/UI/WPF/Converters/HoistingConverters.cs
Normal file
81
src/UI/WPF/Converters/HoistingConverters.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// 索引加1转换器(用于显示序号从1开始)
|
||||
/// </summary>
|
||||
public class IndexPlusOneConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is int index)
|
||||
return index + 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 方向到文本转换器
|
||||
/// </summary>
|
||||
public class DirectionToTextConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return (bool)value ? "上升" : "下降";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 方向到颜色转换器
|
||||
/// </summary>
|
||||
public class DirectionToColorConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return (bool)value ? new SolidColorBrush(Colors.LightGreen) : new SolidColorBrush(Colors.LightCoral);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 目标位置到文本转换器
|
||||
/// </summary>
|
||||
public class TargetToTextConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value == null)
|
||||
return "未设置";
|
||||
|
||||
// 如果值是 Point3D 类型
|
||||
var point = value as Autodesk.Navisworks.Api.Point3D;
|
||||
if (point != null)
|
||||
return $"({point.X:F1}, {point.Y:F1})";
|
||||
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
56
src/UI/WPF/Models/HoistingLevelItem.cs
Normal file
56
src/UI/WPF/Models/HoistingLevelItem.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Autodesk.Navisworks.Api;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// 层级数据项(支持属性变更通知)
|
||||
/// </summary>
|
||||
public class HoistingLevelItem : INotifyPropertyChanged
|
||||
{
|
||||
private int _index;
|
||||
private double _heightInMeters;
|
||||
private Point3D _horizontalTarget;
|
||||
private bool _isRise = true;
|
||||
private string _description;
|
||||
|
||||
public int Index
|
||||
{
|
||||
get => _index;
|
||||
set { _index = value; OnPropertyChanged(nameof(Index)); }
|
||||
}
|
||||
|
||||
public double HeightInMeters
|
||||
{
|
||||
get => _heightInMeters;
|
||||
set { _heightInMeters = value; OnPropertyChanged(nameof(HeightInMeters)); }
|
||||
}
|
||||
|
||||
public Point3D HorizontalTarget
|
||||
{
|
||||
get => _horizontalTarget;
|
||||
set { _horizontalTarget = value; OnPropertyChanged(nameof(HorizontalTarget)); }
|
||||
}
|
||||
|
||||
public bool IsRise
|
||||
{
|
||||
get => _isRise;
|
||||
set { _isRise = value; OnPropertyChanged(nameof(IsRise)); }
|
||||
}
|
||||
|
||||
public string Description
|
||||
{
|
||||
get => _description;
|
||||
set { _description = value; OnPropertyChanged(nameof(Description)); }
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,12 +10,15 @@ using System.Linq;
|
||||
using System.IO;
|
||||
using Microsoft.Win32;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using Autodesk.Navisworks.Api.Plugins;
|
||||
using NavisApplication = Autodesk.Navisworks.Api.Application;
|
||||
using NavisworksTransport.Core;
|
||||
using NavisworksTransport.Core.Config;
|
||||
using NavisworksTransport.Commands;
|
||||
using NavisworksTransport.UI.WPF.Views;
|
||||
using NavisworksTransport.UI.WPF.Models;
|
||||
using NavisworksTransport.Utils;
|
||||
using NavisworksTransport;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
@ -102,6 +105,20 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 资源清理管理
|
||||
private bool _disposed = false;
|
||||
|
||||
// 多层吊装模式
|
||||
private bool _isMultiLevelHoistingMode = false;
|
||||
private ObservableCollection<HoistingLevelItem> _multiLevelItems = new ObservableCollection<HoistingLevelItem>();
|
||||
private Point3D _multiLevelStartPoint;
|
||||
private Point3D _multiLevelEndPoint;
|
||||
private bool _hasMultiLevelStartPoint = false;
|
||||
private bool _hasMultiLevelEndPoint = false;
|
||||
private string _multiLevelStartPointText = "未选择";
|
||||
private string _multiLevelEndPointText = "未选择";
|
||||
private string _multiLevelStatsText = "层级数: 0 | 预估路径点数: 0";
|
||||
private bool _isSelectingMultiLevelStartPoint = false;
|
||||
private bool _isSelectingMultiLevelEndPoint = false;
|
||||
private HoistingLevelItem _currentSelectingLevelTarget;
|
||||
|
||||
#endregion
|
||||
|
||||
#region 公共属性
|
||||
@ -334,6 +351,41 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
set => SetProperty(ref _autoPathEndPoint, value);
|
||||
}
|
||||
|
||||
#region 多层吊装属性
|
||||
|
||||
public bool IsMultiLevelHoistingMode
|
||||
{
|
||||
get => _isMultiLevelHoistingMode;
|
||||
set => SetProperty(ref _isMultiLevelHoistingMode, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<HoistingLevelItem> MultiLevelItems
|
||||
{
|
||||
get => _multiLevelItems;
|
||||
set => SetProperty(ref _multiLevelItems, value);
|
||||
}
|
||||
|
||||
public string MultiLevelStartPointText
|
||||
{
|
||||
get => _multiLevelStartPointText;
|
||||
set => SetProperty(ref _multiLevelStartPointText, value);
|
||||
}
|
||||
|
||||
public string MultiLevelEndPointText
|
||||
{
|
||||
get => _multiLevelEndPointText;
|
||||
set => SetProperty(ref _multiLevelEndPointText, value);
|
||||
}
|
||||
|
||||
public string MultiLevelStatsText
|
||||
{
|
||||
get => _multiLevelStatsText;
|
||||
set => SetProperty(ref _multiLevelStatsText, value);
|
||||
}
|
||||
|
||||
public bool CanCreateMultiLevelPath => _hasMultiLevelStartPoint && _hasMultiLevelEndPoint && _multiLevelItems.Count > 0;
|
||||
|
||||
#endregion
|
||||
|
||||
public double ObjectLength
|
||||
{
|
||||
@ -694,6 +746,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
public ICommand NewPathCommand { get; private set; }
|
||||
public ICommand NewRailPathCommand { get; private set; }
|
||||
public ICommand NewHoistingPathCommand { get; private set; }
|
||||
public ICommand NewMultiLevelHoistingPathCommand { get; private set; }
|
||||
public ICommand DeletePathCommand { get; private set; }
|
||||
public ICommand RenamePathCommand { get; private set; }
|
||||
public ICommand StartEditCommand { get; private set; }
|
||||
@ -710,6 +763,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
public ICommand ImportPathCommand { get; private set; }
|
||||
public ICommand ExportPathCommand { get; private set; }
|
||||
public ICommand SaveAsPathCommand { get; private set; }
|
||||
|
||||
// 多层吊装命令
|
||||
public ICommand SelectMultiLevelStartPointCommand { get; private set; }
|
||||
public ICommand SelectMultiLevelEndPointCommand { get; private set; }
|
||||
public ICommand AddMultiLevelCommand { get; private set; }
|
||||
public ICommand DeleteMultiLevelCommand { get; private set; }
|
||||
public ICommand PickMultiLevelTargetCommand { get; private set; }
|
||||
public ICommand CreateMultiLevelPathCommand { get; private set; }
|
||||
public ICommand CancelMultiLevelHoistingCommand { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@ -945,6 +1007,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
NewPathCommand = new RelayCommand(async () => await ExecuteNewPathAsync(), () => CanExecuteNewPath);
|
||||
NewRailPathCommand = new RelayCommand(async () => await ExecuteNewRailPathAsync(), () => CanExecuteNewRailPath);
|
||||
NewHoistingPathCommand = new RelayCommand(async () => await ExecuteNewHoistingPathAsync());
|
||||
NewMultiLevelHoistingPathCommand = new RelayCommand(async () => await ExecuteNewMultiLevelHoistingPathAsync());
|
||||
SelectMultiLevelStartPointCommand = new RelayCommand(async () => await ExecuteSelectMultiLevelStartPointAsync());
|
||||
SelectMultiLevelEndPointCommand = new RelayCommand(async () => await ExecuteSelectMultiLevelEndPointAsync());
|
||||
AddMultiLevelCommand = new RelayCommand(() => ExecuteAddMultiLevel());
|
||||
DeleteMultiLevelCommand = new RelayCommand<HoistingLevelItem>((item) => ExecuteDeleteMultiLevel(item));
|
||||
PickMultiLevelTargetCommand = new RelayCommand<HoistingLevelItem>((item) => ExecutePickMultiLevelTarget(item));
|
||||
CreateMultiLevelPathCommand = new RelayCommand(async () => await ExecuteCreateMultiLevelPathAsync(), () => CanCreateMultiLevelPath);
|
||||
CancelMultiLevelHoistingCommand = new RelayCommand(() => ExecuteCancelMultiLevelHoisting());
|
||||
DeletePathCommand = new RelayCommand(async () => await ExecuteDeletePathAsync());
|
||||
RenamePathCommand = new RelayCommand(async () => await ExecuteRenamePathAsync());
|
||||
StartEditCommand = new RelayCommand(async () => await ExecuteAddPathPointAsync(), () => CanExecuteStartEdit);
|
||||
@ -1208,7 +1278,558 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
LogManager.Error("路径规划管理器未初始化");
|
||||
}
|
||||
}, "新建吊装路径");
|
||||
} /// <summary>
|
||||
}
|
||||
|
||||
#region 多层吊装页签模式实现
|
||||
|
||||
/// <summary>
|
||||
/// 执行新建多层阶梯式吊装路径命令 - 启用页签模式
|
||||
/// </summary>
|
||||
private async Task ExecuteNewMultiLevelHoistingPathAsync()
|
||||
{
|
||||
await SafeExecuteAsync(() =>
|
||||
{
|
||||
UpdateMainStatus("正在创建多层阶梯式吊装路径...");
|
||||
|
||||
// 清除所有现有路径的可视化显示
|
||||
if (PathPointRenderPlugin.Instance != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all", "grid_visualization_channel", "grid_visualization_unknown", "grid_visualization_obstacle", "grid_visualization_door");
|
||||
LogManager.Info("新建多层吊装路径:已清除现有路径可视化显示(保留网格可视化)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"新建多层吊装路径:清除现有路径可视化失败: {ex.Message}", ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
if (_pathPlanningManager == null)
|
||||
{
|
||||
UpdateMainStatus("路径规划管理器未初始化");
|
||||
LogManager.Error("路径规划管理器未初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
// 启用多层吊装页签模式
|
||||
IsMultiLevelHoistingMode = true;
|
||||
|
||||
// 清空集合和重置状态
|
||||
MultiLevelItems.Clear();
|
||||
_multiLevelStartPoint = null;
|
||||
_multiLevelEndPoint = null;
|
||||
_hasMultiLevelStartPoint = false;
|
||||
_hasMultiLevelEndPoint = false;
|
||||
MultiLevelStartPointText = "未选择";
|
||||
MultiLevelEndPointText = "未选择";
|
||||
UpdateMultiLevelStats();
|
||||
|
||||
// 重置选择状态
|
||||
_isSelectingMultiLevelStartPoint = false;
|
||||
_isSelectingMultiLevelEndPoint = false;
|
||||
_currentSelectingLevelTarget = null;
|
||||
|
||||
// 清理之前的点击事件订阅
|
||||
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
|
||||
|
||||
UpdateMainStatus("多层吊装模式已启用 - 请设置起点、终点并添加层级");
|
||||
LogManager.Info("[多层吊装] 页签模式已启用");
|
||||
|
||||
}, "新建多层吊装路径");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行选择多层吊装起点命令
|
||||
/// </summary>
|
||||
private async Task ExecuteSelectMultiLevelStartPointAsync()
|
||||
{
|
||||
await SafeExecuteAsync(() =>
|
||||
{
|
||||
if (_pathPlanningManager == null) return;
|
||||
|
||||
// 清除之前的起点标记
|
||||
if (_hasMultiLevelStartPoint && PathPointRenderPlugin.Instance != null)
|
||||
{
|
||||
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == "MultiLevelStartPoint");
|
||||
if (existingRoute != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用自动路径规划相同的模式
|
||||
_pathPlanningManager.DisableMouseHandling();
|
||||
|
||||
// 设置状态
|
||||
_isSelectingMultiLevelStartPoint = true;
|
||||
_isSelectingMultiLevelEndPoint = false;
|
||||
_currentSelectingLevelTarget = null;
|
||||
|
||||
// 订阅点击事件
|
||||
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
|
||||
PathClickToolPlugin.MouseClicked += OnMultiLevelHoistingMouseClicked;
|
||||
|
||||
// 强制重新初始化ToolPlugin以获取鼠标焦点
|
||||
ForceReinitializeToolPlugin(subscribeToEvents: false);
|
||||
|
||||
UpdateMainStatus("请在3D视图中点击选择起点(地面起吊位置)...");
|
||||
LogManager.Info("[多层吊装] 开始选择起点");
|
||||
}, "选择多层吊装起点");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行选择多层吊装终点命令
|
||||
/// </summary>
|
||||
private async Task ExecuteSelectMultiLevelEndPointAsync()
|
||||
{
|
||||
await SafeExecuteAsync(() =>
|
||||
{
|
||||
if (_pathPlanningManager == null) return;
|
||||
|
||||
// 清除之前的终点标记
|
||||
if (_hasMultiLevelEndPoint && PathPointRenderPlugin.Instance != null)
|
||||
{
|
||||
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == "MultiLevelEndPoint");
|
||||
if (existingRoute != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用自动路径规划相同的模式
|
||||
_pathPlanningManager.DisableMouseHandling();
|
||||
|
||||
// 设置状态
|
||||
_isSelectingMultiLevelStartPoint = false;
|
||||
_isSelectingMultiLevelEndPoint = true;
|
||||
_currentSelectingLevelTarget = null;
|
||||
|
||||
// 订阅点击事件
|
||||
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
|
||||
PathClickToolPlugin.MouseClicked += OnMultiLevelHoistingMouseClicked;
|
||||
|
||||
// 强制重新初始化ToolPlugin以获取鼠标焦点
|
||||
ForceReinitializeToolPlugin(subscribeToEvents: false);
|
||||
|
||||
UpdateMainStatus("请在3D视图中点击选择终点(地面落地位置)...");
|
||||
LogManager.Info("[多层吊装] 开始选择终点");
|
||||
}, "选择多层吊装终点");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行添加层级命令
|
||||
/// </summary>
|
||||
private void ExecuteAddMultiLevel()
|
||||
{
|
||||
var level = new HoistingLevelItem
|
||||
{
|
||||
Index = MultiLevelItems.Count,
|
||||
HeightInMeters = 3.0,
|
||||
Description = $"层级{MultiLevelItems.Count + 1}"
|
||||
};
|
||||
MultiLevelItems.Add(level);
|
||||
UpdateMultiLevelStats();
|
||||
|
||||
LogManager.Info($"[多层吊装] 添加层级{level.Index + 1}, 高度={level.HeightInMeters}m");
|
||||
UpdateMainStatus($"已添加层级{level.Index + 1},请设置目标位置");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行删除层级命令
|
||||
/// </summary>
|
||||
private void ExecuteDeleteMultiLevel(HoistingLevelItem item)
|
||||
{
|
||||
if (item == null) return;
|
||||
|
||||
// 清除该层级的可视化标记
|
||||
if (PathPointRenderPlugin.Instance != null)
|
||||
{
|
||||
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == $"MultiLevelLevel{item.Index}");
|
||||
if (existingRoute != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
|
||||
}
|
||||
}
|
||||
|
||||
MultiLevelItems.Remove(item);
|
||||
|
||||
// 重新编号
|
||||
for (int i = 0; i < MultiLevelItems.Count; i++)
|
||||
{
|
||||
MultiLevelItems[i].Index = i;
|
||||
}
|
||||
|
||||
UpdateMultiLevelStats();
|
||||
LogManager.Info($"[多层吊装] 删除层级,剩余{MultiLevelItems.Count}个层级");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行拾取层级目标位置命令
|
||||
/// </summary>
|
||||
private void ExecutePickMultiLevelTarget(HoistingLevelItem item)
|
||||
{
|
||||
if (item == null || _pathPlanningManager == null) return;
|
||||
|
||||
// 清除之前的标记
|
||||
if (PathPointRenderPlugin.Instance != null)
|
||||
{
|
||||
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == $"MultiLevelLevel{item.Index}");
|
||||
if (existingRoute != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用自动路径规划相同的模式
|
||||
_pathPlanningManager.DisableMouseHandling();
|
||||
|
||||
// 设置状态
|
||||
_isSelectingMultiLevelStartPoint = false;
|
||||
_isSelectingMultiLevelEndPoint = false;
|
||||
_currentSelectingLevelTarget = item;
|
||||
|
||||
// 订阅点击事件
|
||||
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
|
||||
PathClickToolPlugin.MouseClicked += OnMultiLevelHoistingMouseClicked;
|
||||
|
||||
// 强制重新初始化ToolPlugin以获取鼠标焦点
|
||||
ForceReinitializeToolPlugin(subscribeToEvents: false);
|
||||
|
||||
UpdateMainStatus($"请在3D视图中点击层级{item.Index + 1}的目标位置...");
|
||||
LogManager.Info($"[多层吊装] 开始选择层级{item.Index + 1}的目标位置");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行创建多层吊装路径命令
|
||||
/// </summary>
|
||||
private async Task ExecuteCreateMultiLevelPathAsync()
|
||||
{
|
||||
await SafeExecuteAsync(() =>
|
||||
{
|
||||
// 验证输入
|
||||
if (!_hasMultiLevelStartPoint)
|
||||
{
|
||||
MessageBox.Show("请先选择起点", "验证失败", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_hasMultiLevelEndPoint)
|
||||
{
|
||||
MessageBox.Show("请先选择终点", "验证失败", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MultiLevelItems.Count == 0)
|
||||
{
|
||||
MessageBox.Show("请至少添加一个层级", "验证失败", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证所有层级
|
||||
foreach (var level in MultiLevelItems)
|
||||
{
|
||||
if (level.HeightInMeters <= 0)
|
||||
{
|
||||
MessageBox.Show($"层级{level.Index + 1}的高度必须大于0", "验证失败",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (level.HorizontalTarget == null)
|
||||
{
|
||||
MessageBox.Show($"请为层级{level.Index + 1}设置目标位置", "验证失败",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 创建层级列表
|
||||
var levels = new List<HoistingLevel>();
|
||||
foreach (var item in MultiLevelItems)
|
||||
{
|
||||
levels.Add(new HoistingLevel
|
||||
{
|
||||
Index = item.Index,
|
||||
HeightInMeters = item.HeightInMeters,
|
||||
HorizontalTarget = item.HorizontalTarget,
|
||||
IsRise = true,
|
||||
Description = item.Description
|
||||
});
|
||||
}
|
||||
|
||||
LogManager.Info($"[多层吊装] 创建路径:起点({_multiLevelStartPoint.X:F2},{_multiLevelStartPoint.Y:F2},{_multiLevelStartPoint.Z:F2}), " +
|
||||
$"终点({_multiLevelEndPoint.X:F2},{_multiLevelEndPoint.Y:F2},{_multiLevelEndPoint.Z:F2}), 层级数={levels.Count}");
|
||||
|
||||
// 使用命令创建路径
|
||||
var parameters = new AerialPathParameters
|
||||
{
|
||||
PathType = PathType.Hoisting,
|
||||
HoistingMode = HoistingMode.MultiLevel,
|
||||
StartPoint = _multiLevelStartPoint,
|
||||
EndPoint = _multiLevelEndPoint,
|
||||
HoistingLevels = levels
|
||||
};
|
||||
|
||||
var command = new CreateAerialPathCommand(parameters);
|
||||
var result = command.ExecuteAsync().Result;
|
||||
|
||||
if (result.IsSuccess && result is PathPlanningResult<PathRoute> typedResult)
|
||||
{
|
||||
var route = typedResult.Data;
|
||||
LogManager.Info($"[多层吊装] 路径创建成功: {route.Name}, 点数: {route.Points.Count}");
|
||||
|
||||
// 创建 ViewModel
|
||||
var newPathViewModel = new PathRouteViewModel
|
||||
{
|
||||
Route = route,
|
||||
IsActive = true,
|
||||
PathType = PathType.Hoisting
|
||||
};
|
||||
|
||||
var pathPoints = CreatePathPointViewModelsFromCoreRoute(route);
|
||||
foreach (var point in pathPoints)
|
||||
{
|
||||
newPathViewModel.Points.Add(point);
|
||||
}
|
||||
|
||||
PathRoutes.Add(newPathViewModel);
|
||||
SelectedPathRoute = newPathViewModel;
|
||||
|
||||
UpdateMainStatus($"✓ 多层吊装路径创建成功: {route.Name} - {levels.Count}个层级, {route.Points.Count}个路径点");
|
||||
OnPropertyChanged(nameof(PathRoutes));
|
||||
|
||||
// 退出多层吊装模式
|
||||
ExecuteCancelMultiLevelHoisting();
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateMainStatus($"创建失败: {result.Message}");
|
||||
MessageBox.Show($"创建多层吊装路径失败: {result.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}, "创建多层吊装路径");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行取消多层吊装命令
|
||||
/// </summary>
|
||||
private void ExecuteCancelMultiLevelHoisting()
|
||||
{
|
||||
// 清理事件订阅
|
||||
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
|
||||
|
||||
// 清理可视化标记
|
||||
if (PathPointRenderPlugin.Instance != null)
|
||||
{
|
||||
// 清理起点标记
|
||||
var startRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == "MultiLevelStartPoint");
|
||||
if (startRoute != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.RemovePath(startRoute.Route.Id);
|
||||
}
|
||||
|
||||
// 清理终点标记
|
||||
var endRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == "MultiLevelEndPoint");
|
||||
if (endRoute != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.RemovePath(endRoute.Route.Id);
|
||||
}
|
||||
|
||||
// 清理层级标记
|
||||
for (int i = 0; i < MultiLevelItems.Count; i++)
|
||||
{
|
||||
var levelRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == $"MultiLevelLevel{i}");
|
||||
if (levelRoute != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.RemovePath(levelRoute.Route.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
IsMultiLevelHoistingMode = false;
|
||||
MultiLevelItems.Clear();
|
||||
_multiLevelStartPoint = null;
|
||||
_multiLevelEndPoint = null;
|
||||
_hasMultiLevelStartPoint = false;
|
||||
_hasMultiLevelEndPoint = false;
|
||||
MultiLevelStartPointText = "未选择";
|
||||
MultiLevelEndPointText = "未选择";
|
||||
_isSelectingMultiLevelStartPoint = false;
|
||||
_isSelectingMultiLevelEndPoint = false;
|
||||
_currentSelectingLevelTarget = null;
|
||||
|
||||
// 恢复PathPlanningManager鼠标处理
|
||||
_pathPlanningManager?.EnableMouseHandling();
|
||||
_pathPlanningManager?.StopClickTool();
|
||||
|
||||
UpdateMultiLevelStats();
|
||||
UpdateMainStatus("多层吊装模式已取消");
|
||||
LogManager.Info("[多层吊装] 页签模式已取消");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新多层吊装统计信息
|
||||
/// </summary>
|
||||
private void UpdateMultiLevelStats()
|
||||
{
|
||||
int levelCount = MultiLevelItems.Count;
|
||||
int pointCount = levelCount > 0 ? 2 + levelCount * 2 + 2 : 0;
|
||||
MultiLevelStatsText = $"层级数: {levelCount} | 预估路径点数: {pointCount}";
|
||||
|
||||
// 通知CanCreateMultiLevelPath变更
|
||||
OnPropertyChanged(nameof(CanCreateMultiLevelPath));
|
||||
|
||||
// 通知命令可执行状态变更
|
||||
System.Windows.Input.CommandManager.InvalidateRequerySuggested();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 多层吊装路径鼠标点击处理
|
||||
/// </summary>
|
||||
private void OnMultiLevelHoistingMouseClicked(object sender, PickItemResult e)
|
||||
{
|
||||
if (e?.Point == null) return;
|
||||
|
||||
var point = e.Point;
|
||||
|
||||
if (_isSelectingMultiLevelStartPoint)
|
||||
{
|
||||
// 设置起点
|
||||
_multiLevelStartPoint = point;
|
||||
_hasMultiLevelStartPoint = true;
|
||||
MultiLevelStartPointText = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})";
|
||||
|
||||
// 渲染起点标记
|
||||
RenderMultiLevelPointMarker(point, isStart: true);
|
||||
|
||||
LogManager.Info($"[多层吊装] 起点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})");
|
||||
UpdateMainStatus($"起点已设置,请选择终点或添加层级");
|
||||
|
||||
// 清理事件订阅并停用ToolPlugin
|
||||
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
|
||||
_pathPlanningManager?.EnableMouseHandling();
|
||||
_pathPlanningManager?.StopClickTool();
|
||||
_isSelectingMultiLevelStartPoint = false;
|
||||
|
||||
// 更新统计
|
||||
UpdateMultiLevelStats();
|
||||
}
|
||||
else if (_isSelectingMultiLevelEndPoint)
|
||||
{
|
||||
// 设置终点
|
||||
_multiLevelEndPoint = point;
|
||||
_hasMultiLevelEndPoint = true;
|
||||
MultiLevelEndPointText = $"({point.X:F2}, {point.Y:F2}, {point.Z:F2})";
|
||||
|
||||
// 渲染终点标记
|
||||
RenderMultiLevelPointMarker(point, isStart: false);
|
||||
|
||||
LogManager.Info($"[多层吊装] 终点已设置: ({point.X:F2}, {point.Y:F2}, {point.Z:F2})");
|
||||
UpdateMainStatus($"终点已设置,可以添加层级创建路径");
|
||||
|
||||
// 清理事件订阅并停用ToolPlugin
|
||||
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
|
||||
_pathPlanningManager?.EnableMouseHandling();
|
||||
_pathPlanningManager?.StopClickTool();
|
||||
_isSelectingMultiLevelEndPoint = false;
|
||||
|
||||
// 更新统计
|
||||
UpdateMultiLevelStats();
|
||||
}
|
||||
else if (_currentSelectingLevelTarget != null)
|
||||
{
|
||||
// 设置层级目标位置
|
||||
var targetPoint = new Point3D(point.X, point.Y, point.Z);
|
||||
_currentSelectingLevelTarget.HorizontalTarget = targetPoint;
|
||||
|
||||
// 渲染层级目标标记
|
||||
RenderMultiLevelLevelMarker(targetPoint, _currentSelectingLevelTarget.Index);
|
||||
|
||||
LogManager.Info($"[多层吊装] 层级{_currentSelectingLevelTarget.Index + 1}目标位置已设置: ({point.X:F2}, {point.Y:F2})");
|
||||
UpdateMainStatus($"层级{_currentSelectingLevelTarget.Index + 1}目标位置已设置,可以继续添加层级或点击创建路径");
|
||||
|
||||
// 清理事件订阅并停用ToolPlugin
|
||||
PathClickToolPlugin.MouseClicked -= OnMultiLevelHoistingMouseClicked;
|
||||
_pathPlanningManager?.EnableMouseHandling();
|
||||
_pathPlanningManager?.StopClickTool();
|
||||
_currentSelectingLevelTarget = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 渲染层级目标位置标记
|
||||
/// </summary>
|
||||
private void RenderMultiLevelLevelMarker(Point3D point, int levelIndex)
|
||||
{
|
||||
if (PathPointRenderPlugin.Instance == null) return;
|
||||
|
||||
var routeName = $"MultiLevelLevel{levelIndex}";
|
||||
|
||||
// 移除之前的标记
|
||||
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == routeName);
|
||||
if (existingRoute != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
|
||||
}
|
||||
|
||||
// 创建临时路径
|
||||
var route = new PathRoute(routeName)
|
||||
{
|
||||
Description = $"层级{levelIndex + 1}目标位置"
|
||||
};
|
||||
var pathPoint = new PathPoint
|
||||
{
|
||||
Name = $"层级{levelIndex + 1}",
|
||||
Position = point,
|
||||
Type = PathPointType.WayPoint
|
||||
};
|
||||
route.AddPoint(pathPoint);
|
||||
|
||||
// 渲染点(使用不同颜色区分层级)
|
||||
PathPointRenderPlugin.Instance.RenderPointOnly(route);
|
||||
LogManager.Info($"[多层吊装] 层级{levelIndex + 1}目标标记已渲染");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 渲染多层吊装路径的点标记
|
||||
/// </summary>
|
||||
private void RenderMultiLevelPointMarker(Point3D point, bool isStart)
|
||||
{
|
||||
if (PathPointRenderPlugin.Instance == null) return;
|
||||
|
||||
var routeName = isStart ? "MultiLevelStartPoint" : "MultiLevelEndPoint";
|
||||
var displayName = isStart ? "多层吊装起点" : "多层吊装终点";
|
||||
var pointType = isStart ? PathPointType.StartPoint : PathPointType.EndPoint;
|
||||
|
||||
// 移除之前的标记
|
||||
var existingRoute = PathRoutes.FirstOrDefault(r => r.Route.Name == routeName);
|
||||
if (existingRoute != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.RemovePath(existingRoute.Route.Id);
|
||||
}
|
||||
|
||||
// 创建临时路径
|
||||
var route = new PathRoute(routeName)
|
||||
{
|
||||
Description = displayName
|
||||
};
|
||||
var pathPoint = new PathPoint
|
||||
{
|
||||
Name = displayName,
|
||||
Position = point,
|
||||
Type = pointType
|
||||
};
|
||||
route.AddPoint(pathPoint);
|
||||
|
||||
// 渲染点
|
||||
PathPointRenderPlugin.Instance.RenderPointOnly(route);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 提取并渲染所有空轨模型的基准路径
|
||||
/// </summary>
|
||||
private void ExtractAndRenderRailBaselinePaths()
|
||||
|
||||
@ -30,6 +30,12 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
|
||||
<converters:BoolToBrushConverter x:Key="BoolToBrushConverter"/>
|
||||
<converters:BoolToOpacityConverter x:Key="BoolToOpacityConverter"/>
|
||||
|
||||
<!-- 多层吊装转换器 -->
|
||||
<converters:IndexPlusOneConverter x:Key="IndexPlusOneConverter"/>
|
||||
<converters:DirectionToTextConverter x:Key="DirectionToTextConverter"/>
|
||||
<converters:DirectionToColorConverter x:Key="DirectionToColorConverter"/>
|
||||
<converters:TargetToTextConverter x:Key="TargetToTextConverter"/>
|
||||
|
||||
<!-- 滑块样式 -->
|
||||
<Style x:Key="SliderStyle" TargetType="Slider">
|
||||
<Setter Property="Foreground" Value="{StaticResource NavisworksSecondaryBrush}"/>
|
||||
@ -208,6 +214,10 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
|
||||
Command="{Binding NewHoistingPathCommand}"
|
||||
Style="{StaticResource ActionButtonStyle}"
|
||||
ToolTip="创建吊装路径"/>
|
||||
<Button Content="多层吊装"
|
||||
Command="{Binding NewMultiLevelHoistingPathCommand}"
|
||||
Style="{StaticResource ActionButtonStyle}"
|
||||
ToolTip="创建多层阶梯式吊装路径"/>
|
||||
<Button Content="删除"
|
||||
Command="{Binding DeletePathCommand}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
@ -260,7 +270,151 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 区域3: 路径编辑 -->
|
||||
<!-- 区域3: 多层吊装路径 -->
|
||||
<Border BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="0" Margin="0,10,0,0" Padding="10"
|
||||
Visibility="{Binding IsMultiLevelHoistingMode, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<StackPanel>
|
||||
<Label Content="多层阶梯式吊装路径" Style="{StaticResource SectionHeaderStyle}"/>
|
||||
<TextBlock Text="设置多个高度层级,支持上升和下降的阶梯式移动"
|
||||
FontSize="10" Foreground="#FF666666" Margin="0,-5,0,5"/>
|
||||
|
||||
<!-- 起点/终点设置 -->
|
||||
<Grid Margin="0,5,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 起点 -->
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Button Content="选择起点"
|
||||
Command="{Binding SelectMultiLevelStartPointCommand}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
ToolTip="在3D视图中点击选择地面起吊位置"/>
|
||||
<TextBox Text="{Binding MultiLevelStartPointText, Mode=OneWay}"
|
||||
Style="{StaticResource ReadOnlyTextBoxStyle}"
|
||||
Width="100"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 终点 -->
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal">
|
||||
<Button Content="选择终点"
|
||||
Command="{Binding SelectMultiLevelEndPointCommand}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
ToolTip="在3D视图中点击选择最终放置位置"/>
|
||||
<TextBox Text="{Binding MultiLevelEndPointText, Mode=OneWay}"
|
||||
Style="{StaticResource ReadOnlyTextBoxStyle}"
|
||||
Width="100"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- 层级列表标题 -->
|
||||
<Grid Margin="0,10,0,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="层级列表" VerticalAlignment="Center" FontSize="11" Foreground="#FF666666"/>
|
||||
<Button Grid.Column="1" Content="+ 添加层级"
|
||||
Command="{Binding AddMultiLevelCommand}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
Width="80" Height="24"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 层级列表 -->
|
||||
<DataGrid ItemsSource="{Binding MultiLevelItems}"
|
||||
AutoGenerateColumns="False"
|
||||
CanUserAddRows="False"
|
||||
CanUserDeleteRows="False"
|
||||
SelectionMode="Single"
|
||||
GridLinesVisibility="Horizontal"
|
||||
AlternatingRowBackground="#FFF0F8FF"
|
||||
HeadersVisibility="Column"
|
||||
RowHeaderWidth="0"
|
||||
Height="150">
|
||||
<DataGrid.Columns>
|
||||
<!-- 序号 -->
|
||||
<DataGridTextColumn Header="序号" Binding="{Binding Index, Converter={StaticResource IndexPlusOneConverter}}" Width="45" IsReadOnly="True"/>
|
||||
|
||||
<!-- 高度 -->
|
||||
<DataGridTemplateColumn Header="高度(m)" Width="80">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox Text="{Binding HeightInMeters, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
|
||||
Width="70" Height="22" FontSize="10"
|
||||
VerticalAlignment="Center" VerticalContentAlignment="Center"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
|
||||
<!-- 方向 -->
|
||||
<DataGridTemplateColumn Header="方向" Width="55">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<ToggleButton Content="{Binding IsRise, Converter={StaticResource DirectionToTextConverter}}"
|
||||
IsChecked="{Binding IsRise, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Width="50" Height="22" FontSize="10"
|
||||
Background="{Binding IsRise, Converter={StaticResource DirectionToColorConverter}}"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
|
||||
<!-- 目标位置 -->
|
||||
<DataGridTemplateColumn Header="目标位置" Width="*" MinWidth="100">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{Binding HorizontalTarget, Converter={StaticResource TargetToTextConverter}}"
|
||||
VerticalAlignment="Center" Width="70" FontSize="10"/>
|
||||
<Button Content="📍"
|
||||
Command="{Binding DataContext.PickMultiLevelTargetCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||
CommandParameter="{Binding}"
|
||||
Width="24" Height="22" FontSize="10"
|
||||
ToolTip="拾取目标位置"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
|
||||
<!-- 操作 -->
|
||||
<DataGridTemplateColumn Header="操作" Width="50">
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<Button Content="×"
|
||||
Command="{Binding DataContext.DeleteMultiLevelCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||
CommandParameter="{Binding}"
|
||||
Width="24" Height="22"
|
||||
Foreground="Red" FontWeight="Bold"/>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<!-- 统计信息和创建按钮 -->
|
||||
<Grid Margin="0,10,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="{Binding MultiLevelStatsText}"
|
||||
VerticalAlignment="Center" FontSize="10" Foreground="#FF666666"/>
|
||||
<Button Grid.Column="1" Content="创建路径"
|
||||
Command="{Binding CreateMultiLevelPathCommand}"
|
||||
Style="{StaticResource ActionButtonStyle}"
|
||||
IsEnabled="{Binding CanCreateMultiLevelPath}"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 取消按钮 -->
|
||||
<Button Content="取消多层吊装"
|
||||
Command="{Binding CancelMultiLevelHoistingCommand}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="0,10,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 区域4: 路径编辑 -->
|
||||
<Border BorderBrush="#FFD4E7FF" BorderThickness="1" CornerRadius="0" Margin="0,10,0,0" Padding="10">
|
||||
<StackPanel>
|
||||
<Label Content="路径编辑" Style="{StaticResource SectionHeaderStyle}"/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user