新增空轨数据结构、自动提取基准路径并可视化、路径点吸附空轨基准路径功能
This commit is contained in:
parent
0a2e29cee9
commit
8424503576
@ -181,6 +181,7 @@
|
||||
<Compile Include="src\PathPlanning\VoxelPathFinder.cs" />
|
||||
<Compile Include="src\PathPlanning\MeshSDFTester.cs" />
|
||||
<Compile Include="src\PathPlanning\VoxelGridVisualizer.cs" />
|
||||
<Compile Include="src\PathPlanning\RailGeometryHelper.cs" />
|
||||
<!-- UI - WPF -->
|
||||
<Compile Include="src\UI\WPF\Views\LogisticsControlPanel.xaml.cs">
|
||||
<DependentUpon>LogisticsControlPanel.xaml</DependentUpon>
|
||||
@ -254,6 +255,7 @@
|
||||
<Compile Include="src\UI\WPF\Converters\BoolToVisibilityConverter.cs" />
|
||||
<Compile Include="src\UI\WPF\Converters\IndexConverter.cs" />
|
||||
<Compile Include="src\UI\WPF\Converters\CountToVisibilityConverter.cs" />
|
||||
<Compile Include="src\UI\WPF\Converters\PathTypeConverter.cs" />
|
||||
<Compile Include="src\UI\WPF\Models\LogisticsModel.cs" />
|
||||
<Compile Include="src\UI\WPF\Models\PathRouteViewModel.cs" />
|
||||
<Compile Include="src\UI\WPF\Models\SplitPreviewItem.cs" />
|
||||
|
||||
522
doc/working/空轨支持方案.md
Normal file
522
doc/working/空轨支持方案.md
Normal file
@ -0,0 +1,522 @@
|
||||
# 空轨支持方案
|
||||
|
||||
## 一、需求分析
|
||||
|
||||
### 1.1 核心需求
|
||||
|
||||
1. **新增空轨属性**:在物流属性系统中增加"空轨"类型
|
||||
2. **空轨路径生成**:
|
||||
- 空轨走向固定,只需设置起点和终点
|
||||
- 自动提取空轨几何体的下表面中心线作为路径
|
||||
3. **路径类型区分**:路径需要标记为"地面路径"或"空轨路径"
|
||||
4. **动画运行模式**:
|
||||
- 地面路径:车辆中心点在路径点上
|
||||
- 空轨路径:车辆悬挂在空轨下方运行(车辆中心 = 路径点 - 车辆高度/2)
|
||||
5. **碰撞检测**:
|
||||
- 统一使用3D碰撞检测
|
||||
- 空轨路径需要排除空轨本身
|
||||
6. **限制条件**:空轨暂时只支持人工路径,不支持自动路径规划
|
||||
|
||||
### 1.2 使用场景
|
||||
|
||||
- **地面路径**:转运车在楼面、走廊、通道上运行
|
||||
- **空轨路径**:悬挂式输送设备在空轨上运行
|
||||
|
||||
### 1.3 已确认信息
|
||||
|
||||
1. ✅ **空轨模型**:已存在,不需要建模规范
|
||||
2. ✅ **空轨类型**:不需要分类型,只有一个"空轨"类型
|
||||
3. ✅ **车辆悬挂**:车辆悬挂在空轨下方
|
||||
4. ✅ **碰撞检测**:需要排除空轨本身
|
||||
5. ✅ **数据库迁移**:不需要,新字段使用默认值
|
||||
6. ✅ **向后兼容**:不需要向后兼容性
|
||||
7. ✅ **碰撞检测统一**:地面路径和空轨路径都使用3D碰撞检测
|
||||
8. ✅ **轨道几何**:轨道不是水平的,需要精确计算几何体数据
|
||||
|
||||
---
|
||||
|
||||
## 二、架构影响分析
|
||||
|
||||
### 2.1 涉及的核心模块
|
||||
|
||||
| 模块 | 影响程度 | 改动内容 |
|
||||
|------|---------|---------|
|
||||
| **物流属性系统** | 🟢 低 | 新增"空轨"枚举值 |
|
||||
| **路径数据模型** | 🟡 中 | 新增路径类型字段 |
|
||||
| **路径规划系统** | 🟡 中 | 新增空轨路径生成逻辑 |
|
||||
| **动画系统** | 🟡 中 | 根据路径类型调整车辆位置 |
|
||||
| **碰撞检测系统** | 🟢 低 | 空轨路径排除空轨本身 |
|
||||
| **UI界面** | 🟡 中 | 路径类型选择、空轨路径生成 |
|
||||
|
||||
---
|
||||
|
||||
## 三、详细实施方案
|
||||
|
||||
### 3.1 第一阶段:基础数据结构(1天)
|
||||
|
||||
#### 3.1.1 新增空轨属性
|
||||
|
||||
**文件**:`CategoryAttributeManager.cs`
|
||||
|
||||
```csharp
|
||||
public enum LogisticsElementType
|
||||
{
|
||||
// ... 现有类型 ...
|
||||
|
||||
/// <summary>
|
||||
/// 空轨 - 空中运输路径,车辆悬挂运行,权重0.7
|
||||
/// </summary>
|
||||
空轨 = 9,
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.1.2 路径类型枚举
|
||||
|
||||
**文件**:`PathPlanningModels.cs`
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// 路径类型
|
||||
/// </summary>
|
||||
public enum PathType
|
||||
{
|
||||
/// <summary>
|
||||
/// 地面路径 - 车辆在地面运行
|
||||
/// </summary>
|
||||
Ground = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 空轨路径 - 车辆悬挂在空轨下方运行
|
||||
/// </summary>
|
||||
Rail = 1,
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.1.3 路径数据模型扩展
|
||||
|
||||
**文件**:`PathRouteViewModel.cs`
|
||||
|
||||
```csharp
|
||||
private PathType _pathType = PathType.Ground;
|
||||
|
||||
public PathType PathType
|
||||
{
|
||||
get => _pathType;
|
||||
set => SetProperty(ref _pathType, value);
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.1.4 路径数据库适配
|
||||
|
||||
**文件**:`PathDatabase.cs`
|
||||
|
||||
```csharp
|
||||
// 保存路径时,保存路径类型
|
||||
public void SavePath(PathRouteViewModel path)
|
||||
{
|
||||
// ... 现有逻辑 ...
|
||||
// 新增:保存路径类型
|
||||
}
|
||||
|
||||
// 加载路径时,读取路径类型
|
||||
public PathRouteViewModel LoadPath(string pathId)
|
||||
{
|
||||
// ... 现有逻辑 ...
|
||||
// 新增:读取路径类型
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 第二阶段:空轨路径生成(2天)
|
||||
|
||||
#### 3.2.1 复用现有几何计算
|
||||
|
||||
**现有代码复用**:
|
||||
|
||||
- `ChannelHeightDetector.AnalyzeChannelGeometry()` - 分析通道几何
|
||||
- `ChannelHeightDetector.CalculatePreciseHeightAtPosition()` - 计算精确高度
|
||||
- `GeometryHelper.ExtractTriangles()` - 提取三角形
|
||||
- `GeometryHelper.RayTriangleIntersect()` - 射线-三角形交点检测
|
||||
|
||||
#### 3.2.2 新增底部高度检测方法
|
||||
|
||||
**文件**:`ChannelHeightDetector.cs`
|
||||
|
||||
新增方法:
|
||||
|
||||
- `GetChannelBottomHeight(Point3D position, IEnumerable<ModelItem> channelItems)` - 获取通道底面高度
|
||||
- `CalculateBottomPreciseHeightAtPosition(Point3D position, ChannelHeightInfo heightInfo, ModelItem channel)` - 计算底面精确高度
|
||||
- `TryRaycastFromBelow(Point3D position, ModelItem channel, double floorHeight, out double height)` - 从下方射线投射
|
||||
- `PerformRayTriangleIntersectionFromBelow(Point3D position, List<Triangle3D> triangles)` - 从下往上射线-三角形交点检测
|
||||
- `TryGetGeometricBottomHeight(Point3D position, ModelItem channel, out double height)` - View API获取底面高度
|
||||
- `AnalyzeBottomSurfaceHeightAtPosition(Point3D position, ModelItem channel, double floorHeight, double ceilingHeight)` - 分析底面表面高度
|
||||
- `TryMultiPointBottomSampling(Point3D position, ModelItem channel)` - 多点采样获取底面高度
|
||||
|
||||
#### 3.2.3 空轨中心线提取工具
|
||||
|
||||
**文件**:`RailGeometryHelper.cs`(新建)
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// 空轨几何体辅助工具
|
||||
/// 负责提取空轨的下表面中心线
|
||||
/// </summary>
|
||||
public static class RailGeometryHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 从空轨几何体提取下表面中心线
|
||||
/// </summary>
|
||||
/// <param name="railModel">空轨模型项</param>
|
||||
/// <param name="startPoint">起点(2D坐标)</param>
|
||||
/// <param name="endPoint">终点(2D坐标)</param>
|
||||
/// <returns>路径点列表(包含精确的Z坐标)</returns>
|
||||
public static List<Point3D> ExtractRailCenterLine(
|
||||
ModelItem railModel,
|
||||
Point3D startPoint,
|
||||
Point3D endPoint)
|
||||
{
|
||||
var pathPoints = new List<Point3D>();
|
||||
|
||||
try
|
||||
{
|
||||
LogManager.Info($"[空轨] 开始提取空轨中心线: {railModel.DisplayName}");
|
||||
|
||||
// 1. 创建高度检测器实例
|
||||
var heightDetector = new ChannelHeightDetector();
|
||||
var railItems = new List<ModelItem> { railModel };
|
||||
|
||||
// 2. 计算起点和终点在X-Y平面上的投影距离
|
||||
double distance2D = Math.Sqrt(
|
||||
Math.Pow(endPoint.X - startPoint.X, 2) +
|
||||
Math.Pow(endPoint.Y - startPoint.Y, 2)
|
||||
);
|
||||
|
||||
if (distance2D < 0.01)
|
||||
{
|
||||
LogManager.Warning("[空轨] 起点和终点重合");
|
||||
return pathPoints;
|
||||
}
|
||||
|
||||
// 3. 在起点和终点之间采样,计算每个采样点的精确Z坐标
|
||||
int sampleCount = Math.Max(2, (int)Math.Ceiling(distance2D / 0.5)); // 每0.5米一个采样点
|
||||
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
{
|
||||
double t = (double)i / (sampleCount - 1);
|
||||
|
||||
// 线性插值计算X、Y坐标
|
||||
double x = startPoint.X + t * (endPoint.X - startPoint.X);
|
||||
double y = startPoint.Y + t * (endPoint.Y - startPoint.Y);
|
||||
double z = startPoint.Z; // 初始Z坐标
|
||||
|
||||
// 调用现有的GetChannelBottomHeight方法获取精确底面高度
|
||||
var samplePoint = new Point3D(x, y, z); // Z坐标会被方法更新
|
||||
var bottomHeight = heightDetector.GetChannelBottomHeight(samplePoint, railItems);
|
||||
|
||||
pathPoints.Add(new Point3D(x, y, bottomHeight));
|
||||
}
|
||||
|
||||
LogManager.Info($"[空轨] 生成 {pathPoints.Count} 个路径点,总长度: {distance2D:F2}m");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[空轨] 提取空轨中心线失败: {ex.Message}");
|
||||
}
|
||||
|
||||
return pathPoints;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.4 路径编辑器扩展
|
||||
|
||||
**文件**:`PathEditingViewModel.cs`
|
||||
|
||||
新增方法:
|
||||
|
||||
- `GeneratePathFromRail(ModelItem railModel, Point3D startPoint, Point3D endPoint)` - 从空轨生成路径
|
||||
- `SelectRailModelCommand` - 选择空轨命令
|
||||
- `SetPathStartPointCommand` - 设置起点命令
|
||||
- `SetPathEndPointCommand` - 设置终点命令
|
||||
- `GeneratePathFromRailCommand` - 生成路径命令
|
||||
|
||||
新增属性:
|
||||
|
||||
- `SelectedPathType` - 选中的路径类型
|
||||
- `PathTypes` - 路径类型数组
|
||||
- `IsRailPathType` - 是否为空轨路径类型
|
||||
- `SelectedRailModel` - 选中的空轨模型
|
||||
- `SelectedRailModelName` - 选中的空轨模型名称
|
||||
- `PathStartPoint` - 路径起点
|
||||
- `PathStartPointDisplay` - 路径起点显示
|
||||
- `PathEndPoint` - 路径终点
|
||||
- `PathEndPointDisplay` - 路径终点显示
|
||||
|
||||
---
|
||||
|
||||
### 3.3 第三阶段:动画系统适配(1天)
|
||||
|
||||
#### 3.3.1 车辆位置计算
|
||||
|
||||
**文件**:`PathAnimationManager.cs`
|
||||
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// 计算车辆在路径点上的位置
|
||||
/// </summary>
|
||||
private Point3D CalculateVehiclePosition(Point3D pathPoint, PathType pathType)
|
||||
{
|
||||
if (pathType == PathType.Rail)
|
||||
{
|
||||
// 空轨路径:车辆悬挂在空轨下方
|
||||
// 车辆中心点 = 空轨点 - 车辆高度的一半
|
||||
double vehicleCenterZ = pathPoint.Z - _virtualVehicleHeight / 2;
|
||||
return new Point3D(pathPoint.X, pathPoint.Y, vehicleCenterZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 地面路径:车辆中心点在路径点上
|
||||
return pathPoint;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.3.2 传递路径类型
|
||||
|
||||
**文件**:`PathAnimationManager.cs`
|
||||
|
||||
修改方法:
|
||||
|
||||
- `GenerateAnimationFrames(List<Point3D> pathPoints, PathType pathType)` - 传递路径类型参数
|
||||
- `PrecomputeCollisions(List<Point3D> pathPoints, PathType pathType, List<ModelItem> railModels)` - 传递路径类型和空轨模型列表
|
||||
- `DetectFrameCollisions(AnimationFrame frame, PathType pathType, List<ModelItem> collisionExclusions)` - 传递路径类型和排除列表
|
||||
|
||||
---
|
||||
|
||||
### 3.4 第四阶段:碰撞检测适配(1天)
|
||||
|
||||
#### 3.4.1 空轨排除逻辑
|
||||
|
||||
**文件**:`PathAnimationManager.cs`
|
||||
|
||||
修改方法:
|
||||
|
||||
- `PrecomputeCollisions(List<Point3D> pathPoints, PathType pathType, List<ModelItem> railModels)` - 新增空轨排除列表
|
||||
- `DetectFrameCollisions(AnimationFrame frame, PathType pathType, List<ModelItem> collisionExclusions)` - 使用排除列表过滤
|
||||
|
||||
**核心逻辑**:
|
||||
|
||||
```csharp
|
||||
// 创建碰撞排除列表
|
||||
var collisionExclusions = new List<ModelItem>();
|
||||
|
||||
// 如果是空轨路径,将空轨模型加入排除列表
|
||||
if (pathType == PathType.Rail && railModels != null)
|
||||
{
|
||||
collisionExclusions.AddRange(railModels);
|
||||
LogManager.Info($"[碰撞检测] 已排除 {railModels.Count} 个空轨模型");
|
||||
}
|
||||
|
||||
// 在碰撞检测循环中跳过排除列表中的项目
|
||||
foreach (var modelItem in allModelItems)
|
||||
{
|
||||
// 跳过排除列表中的项目(包括空轨本身)
|
||||
if (collisionExclusions.Contains(modelItem))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 统一使用3D碰撞检测
|
||||
double distance = BoundingBoxGeometryUtils.CalculateDistance(vehicleBoundingBox, modelBoundingBox);
|
||||
|
||||
if (distance <= 0) // 相交或接触
|
||||
{
|
||||
collisions.Add(new CollisionResult
|
||||
{
|
||||
Item1 = _animatedObject,
|
||||
Item2 = modelItem,
|
||||
Distance = distance
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.5 第五阶段:UI界面适配(1天)
|
||||
|
||||
#### 3.5.1 路径类型选择
|
||||
|
||||
**文件**:`PathEditingViewModel.cs`
|
||||
|
||||
新增属性:
|
||||
|
||||
- `PathTypes` - 路径类型数组
|
||||
- `SelectedPathType` - 选中的路径类型
|
||||
- `IsRailPathType` - 是否为空轨路径类型
|
||||
|
||||
#### 3.5.2 命令
|
||||
|
||||
**文件**:`PathEditingViewModel.cs`
|
||||
|
||||
新增命令:
|
||||
|
||||
- `GeneratePathFromRailCommand` - 从空轨生成路径命令
|
||||
- `SelectRailModelCommand` - 选择空轨命令
|
||||
- `SetPathStartPointCommand` - 设置起点命令
|
||||
- `SetPathEndPointCommand` - 设置终点命令
|
||||
|
||||
#### 3.5.3 UI布局
|
||||
|
||||
**文件**:`LogisticsControlPanel.xaml`
|
||||
|
||||
新增控件:
|
||||
|
||||
- 路径类型选择 ComboBox
|
||||
- 空轨选择 StackPanel(仅在选择空轨类型时显示)
|
||||
- 选择空轨按钮
|
||||
- 设置起点按钮
|
||||
- 设置终点按钮
|
||||
- 生成路径按钮
|
||||
|
||||
---
|
||||
|
||||
## 四、实施步骤
|
||||
|
||||
### 第一阶段:基础数据结构(1天)
|
||||
|
||||
1. ✅ 新增"空轨"物流属性类型
|
||||
2. ✅ 新增路径类型枚举
|
||||
3. ✅ 扩展路径数据模型
|
||||
4. ✅ 适配数据库读写
|
||||
|
||||
### 第二阶段:空轨路径生成(2天)
|
||||
|
||||
1. ✅ 在 `ChannelHeightDetector` 中新增底部高度检测方法
|
||||
2. ✅ 复用现有的几何计算代码
|
||||
3. ✅ 实现 `RailGeometryHelper`(调用现有方法)
|
||||
4. ✅ 扩展路径编辑器
|
||||
5. ✅ UI界面适配
|
||||
6. ✅ 集成测试
|
||||
|
||||
### 第三阶段:动画系统适配(1天)
|
||||
|
||||
1. ✅ 车辆位置计算适配
|
||||
2. ✅ 传递路径类型参数
|
||||
3. ✅ 动画播放测试
|
||||
|
||||
### 第四阶段:碰撞检测适配(1天)
|
||||
|
||||
1. ✅ 实现空轨排除逻辑
|
||||
2. ✅ 统一使用3D碰撞检测
|
||||
3. ✅ 碰撞报告适配
|
||||
4. ✅ 集成测试
|
||||
|
||||
### 第五阶段:测试和优化(1天)
|
||||
|
||||
1. ✅ 功能测试
|
||||
2. ✅ 性能测试
|
||||
3. ✅ 用户验收测试
|
||||
|
||||
**总计**:6天
|
||||
|
||||
---
|
||||
|
||||
## 五、风险评估
|
||||
|
||||
| 风险项 | 风险等级 | 缓解措施 |
|
||||
|--------|---------|---------|
|
||||
| 空轨几何体提取失败 | 🟢 低 | 空轨模型已存在,直接使用 |
|
||||
| 车辆悬挂位置计算错误 | 🟡 中 | 充分测试,提供可视化验证 |
|
||||
| 碰撞检测误报 | 🟡 中 | 排除空轨本身,调整检测参数 |
|
||||
| 性能下降 | 🟢 低 | 空轨路径数量少,影响有限 |
|
||||
| 几何计算性能 | 🟡 中 | 复用现有代码,优化采样策略 |
|
||||
|
||||
---
|
||||
|
||||
## 六、总结
|
||||
|
||||
### 6.1 方案优势
|
||||
|
||||
- ✅ **代码复用最大化**:充分利用现有的几何计算代码
|
||||
- ✅ **架构统一**:空轨和地面路径使用相同的高度检测框架
|
||||
- ✅ **改动最小化**:只需新增底部检测方法,其他全部复用
|
||||
- ✅ **易于维护**:几何计算逻辑集中管理
|
||||
- ✅ **向后兼容**:不需要迁移,新字段使用默认值
|
||||
- ✅ **碰撞检测统一**:统一使用3D检测,简化逻辑
|
||||
|
||||
### 6.2 关键技术点
|
||||
|
||||
1. **复用现有代码**:`ChannelHeightDetector` 的几何计算方法
|
||||
2. **新增底部检测**:`GetChannelBottomHeight()` 方法
|
||||
3. **车辆悬挂计算**:车辆中心 = 空轨点 - 车辆高度/2
|
||||
4. **碰撞检测统一**:统一使用3D检测
|
||||
5. **排除空轨本身**:通过排除列表过滤空轨模型
|
||||
6. **精确几何计算**:支持倾斜、弯曲的轨道
|
||||
|
||||
### 6.3 技术难点
|
||||
|
||||
- 需要处理大量三角形面片(性能优化)
|
||||
- 需要处理重叠三角形(选择最高点)
|
||||
- 需要处理采样点不在任何三角形内的情况(使用默认值)
|
||||
|
||||
### 6.4 预期成果
|
||||
|
||||
- ✅ 支持空轨路径生成
|
||||
- ✅ 支持车辆悬挂运行
|
||||
- ✅ 支持空轨碰撞检测(排除空轨本身)
|
||||
- ✅ 保持与现有地面路径功能的兼容性
|
||||
|
||||
---
|
||||
|
||||
## 七、附录
|
||||
|
||||
### 7.1 文件清单
|
||||
|
||||
#### 新增文件
|
||||
|
||||
- `RailGeometryHelper.cs` - 空轨几何体辅助工具
|
||||
|
||||
#### 修改文件
|
||||
|
||||
- `CategoryAttributeManager.cs` - 新增空轨枚举
|
||||
- `PathPlanningModels.cs` - 新增路径类型枚举
|
||||
- `PathRouteViewModel.cs` - 新增路径类型字段
|
||||
- `PathDatabase.cs` - 适配路径类型读写
|
||||
- `ChannelHeightDetector.cs` - 新增底部高度检测方法
|
||||
- `PathEditingViewModel.cs` - 扩展路径编辑功能
|
||||
- `PathAnimationManager.cs` - 适配车辆位置和碰撞检测
|
||||
- `LogisticsControlPanel.xaml` - UI界面适配
|
||||
|
||||
### 7.2 方法清单
|
||||
|
||||
#### 新增方法(ChannelHeightDetector.cs)
|
||||
|
||||
- `GetChannelBottomHeight(Point3D position, IEnumerable<ModelItem> channelItems)` - 获取通道底面高度
|
||||
- `CalculateBottomPreciseHeightAtPosition(Point3D position, ChannelHeightInfo heightInfo, ModelItem channel)` - 计算底面精确高度
|
||||
- `TryRaycastFromBelow(Point3D position, ModelItem channel, double floorHeight, out double height)` - 从下方射线投射
|
||||
- `PerformRayTriangleIntersectionFromBelow(Point3D position, List<Triangle3D> triangles)` - 从下往上射线-三角形交点检测
|
||||
- `TryGetGeometricBottomHeight(Point3D position, ModelItem channel, out double height)` - View API获取底面高度
|
||||
- `AnalyzeBottomSurfaceHeightAtPosition(Point3D position, ModelItem channel, double floorHeight, double ceilingHeight)` - 分析底面表面高度
|
||||
- `TryMultiPointBottomSampling(Point3D position, ModelItem channel)` - 多点采样获取底面高度
|
||||
|
||||
#### 新增方法(RailGeometryHelper.cs)
|
||||
|
||||
- `ExtractRailCenterLine(ModelItem railModel, Point3D startPoint, Point3D endPoint)` - 提取空轨中心线
|
||||
|
||||
#### 新增方法(PathEditingViewModel.cs)
|
||||
|
||||
- `GeneratePathFromRail(ModelItem railModel, Point3D startPoint, Point3D endPoint)` - 从空轨生成路径
|
||||
|
||||
#### 新增方法(PathAnimationManager.cs)
|
||||
|
||||
- `CalculateVehiclePosition(Point3D pathPoint, PathType pathType)` - 计算车辆位置
|
||||
|
||||
#### 修改方法(PathAnimationManager.cs)
|
||||
|
||||
- `GenerateAnimationFrames(List<Point3D> pathPoints, PathType pathType)` - 传递路径类型参数
|
||||
- `PrecomputeCollisions(List<Point3D> pathPoints, PathType pathType, List<ModelItem> railModels)` - 传递路径类型和空轨模型列表
|
||||
- `DetectFrameCollisions(AnimationFrame frame, PathType pathType, List<ModelItem> collisionExclusions)` - 传递路径类型和排除列表
|
||||
|
||||
---
|
||||
@ -82,6 +82,7 @@ namespace NavisworksTransport
|
||||
MaxVehicleHeight REAL,
|
||||
SafetyMargin REAL,
|
||||
GridSize REAL,
|
||||
PathType INTEGER,
|
||||
CreatedTime DATETIME,
|
||||
LastModified DATETIME
|
||||
)
|
||||
@ -221,8 +222,8 @@ namespace NavisworksTransport
|
||||
// 保存路径基本信息
|
||||
var sql = @"
|
||||
INSERT OR REPLACE INTO PathRoutes
|
||||
(Id, Name, TotalLength, EstimatedTime, TurnRadius, IsCurved, MaxVehicleLength, MaxVehicleWidth, MaxVehicleHeight, SafetyMargin, GridSize, CreatedTime, LastModified)
|
||||
VALUES (@id, @name, @length, @time, @turnRadius, @isCurved, @maxLength, @maxWidth, @maxHeight, @safetyMargin, @gridSize, @created, @modified)
|
||||
(Id, Name, TotalLength, EstimatedTime, TurnRadius, IsCurved, MaxVehicleLength, MaxVehicleWidth, MaxVehicleHeight, SafetyMargin, GridSize, PathType, CreatedTime, LastModified)
|
||||
VALUES (@id, @name, @length, @time, @turnRadius, @isCurved, @maxLength, @maxWidth, @maxHeight, @safetyMargin, @gridSize, @pathType, @created, @modified)
|
||||
";
|
||||
|
||||
using (var cmd = new SQLiteCommand(sql, _connection))
|
||||
@ -238,6 +239,7 @@ namespace NavisworksTransport
|
||||
cmd.Parameters.AddWithValue("@maxHeight", route.MaxVehicleHeight);
|
||||
cmd.Parameters.AddWithValue("@safetyMargin", route.SafetyMargin);
|
||||
cmd.Parameters.AddWithValue("@gridSize", route.GridSize);
|
||||
cmd.Parameters.AddWithValue("@pathType", (int)route.PathType);
|
||||
cmd.Parameters.AddWithValue("@created", route.CreatedTime);
|
||||
cmd.Parameters.AddWithValue("@modified", DateTime.Now);
|
||||
cmd.ExecuteNonQuery();
|
||||
@ -822,6 +824,7 @@ namespace NavisworksTransport
|
||||
MaxVehicleHeight = reader.IsDBNull(reader.GetOrdinal("MaxVehicleHeight")) ? 2.0 : Convert.ToDouble(reader["MaxVehicleHeight"]),
|
||||
SafetyMargin = reader.IsDBNull(reader.GetOrdinal("SafetyMargin")) ? 0.5 : Convert.ToDouble(reader["SafetyMargin"]),
|
||||
GridSize = reader.IsDBNull(reader.GetOrdinal("GridSize")) ? 0.5 : Convert.ToDouble(reader["GridSize"]),
|
||||
PathType = reader.IsDBNull(reader.GetOrdinal("PathType")) ? PathType.Ground : (PathType)Convert.ToInt32(reader["PathType"]),
|
||||
CreatedTime = Convert.ToDateTime(reader["CreatedTime"]),
|
||||
LastModified = Convert.ToDateTime(reader["LastModified"])
|
||||
};
|
||||
|
||||
@ -47,6 +47,9 @@ namespace NavisworksTransport
|
||||
// 自动路径规划模式标志
|
||||
private bool _isInAutoPathMode = false;
|
||||
|
||||
// 空轨吸附模式标志
|
||||
private bool _enableRailSnapping = false;
|
||||
|
||||
// 路径点3D标记管理
|
||||
private List<PathPointMarker> _pathPointMarkers;
|
||||
|
||||
@ -848,18 +851,29 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
/// <param name="pointType">点击类型</param>
|
||||
public void StartClickTool(PathPointType pointType)
|
||||
{
|
||||
StartClickTool(pointType, enableRailSnapping: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动点击工具(支持空轨吸附)
|
||||
/// </summary>
|
||||
/// <param name="pointType">点击类型</param>
|
||||
/// <param name="enableRailSnapping">是否启用空轨吸附</param>
|
||||
public void StartClickTool(PathPointType pointType, bool enableRailSnapping)
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentPointType = pointType;
|
||||
_enableRailSnapping = enableRailSnapping;
|
||||
|
||||
// 检查是否在自动路径模式 - 如果是,则不订阅PathPlanningManager的事件
|
||||
bool shouldSubscribeToEvents = !IsInAutoPathMode;
|
||||
LogManager.Info($"StartClickTool - 自动路径模式: {IsInAutoPathMode}, 订阅事件: {shouldSubscribeToEvents}");
|
||||
LogManager.Info($"StartClickTool - 自动路径模式: {IsInAutoPathMode}, 订阅事件: {shouldSubscribeToEvents}, 空轨吸附: {enableRailSnapping}");
|
||||
|
||||
ActivateToolPlugin(shouldSubscribeToEvents);
|
||||
PathEditState = PathEditState.AddingPoints;
|
||||
LogManager.Info($"点击工具已启动,类型: {pointType},事件订阅: {shouldSubscribeToEvents}");
|
||||
LogManager.Info($"点击工具已启动,类型: {pointType},事件订阅: {shouldSubscribeToEvents},空轨吸附: {enableRailSnapping}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1466,20 +1480,25 @@ namespace NavisworksTransport
|
||||
/// 开始新建路径
|
||||
/// </summary>
|
||||
/// <param name="routeName">路径名称</param>
|
||||
/// <param name="isRailPath">是否为空轨路径</param>
|
||||
/// <returns>创建的新路径</returns>
|
||||
public PathRoute StartCreatingNewRoute(string routeName = null)
|
||||
public PathRoute StartCreatingNewRoute(string routeName = null, bool isRailPath = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 自动选择所有可通行的物流模型(先检查是否有可通行的模型)
|
||||
AutoSelectLogisticsChannels();
|
||||
|
||||
// 检查是否有可通行的物流模型
|
||||
if (_walkableAreas == null || _walkableAreas.Count == 0)
|
||||
// 空轨路径不需要自动选择可通行物流模型
|
||||
if (!isRailPath)
|
||||
{
|
||||
RaiseErrorOccurred("没有找到任何可通行的物流模型,请先为模型设置可通行的物流属性");
|
||||
// 不需要重置状态,因为还没有进入创建状态
|
||||
return null;
|
||||
// 自动选择所有可通行的物流模型(先检查是否有可通行的模型)
|
||||
AutoSelectLogisticsChannels();
|
||||
|
||||
// 检查是否有可通行的物流模型
|
||||
if (_walkableAreas == null || _walkableAreas.Count == 0)
|
||||
{
|
||||
RaiseErrorOccurred("没有找到任何可通行的物流模型,请先为模型设置可通行的物流属性");
|
||||
// 不需要重置状态,因为还没有进入创建状态
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置为创建状态
|
||||
@ -1493,7 +1512,11 @@ namespace NavisworksTransport
|
||||
// 智能管理ToolPlugin状态
|
||||
ManageToolPluginForEditState();
|
||||
|
||||
RaiseStatusChanged($"正在新建路径: {newRoute.Name} - 请在3D视图中可通行的物流模型上点击设置路径点", PathPlanningStatusType.Info);
|
||||
string statusMessage = isRailPath
|
||||
? $"正在新建空轨路径: {newRoute.Name} - 请在3D视图中点击空轨模型设置路径点(将自动吸附到基准路径)"
|
||||
: $"正在新建路径: {newRoute.Name} - 请在3D视图中可通行的物流模型上点击设置路径点";
|
||||
|
||||
RaiseStatusChanged(statusMessage, PathPlanningStatusType.Info);
|
||||
|
||||
return newRoute;
|
||||
}
|
||||
@ -1537,8 +1560,8 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是创建模式,将当前路径添加到路径集合
|
||||
if (_pathEditState == PathEditState.Creating && CurrentRoute != null)
|
||||
// 如果是创建模式或添加点模式,将当前路径添加到路径集合
|
||||
if ((PathEditState == PathEditState.Creating || PathEditState == PathEditState.AddingPoints) && CurrentRoute != null)
|
||||
{
|
||||
if (!_routes.Contains(CurrentRoute))
|
||||
{
|
||||
@ -2669,78 +2692,169 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
private void ProcessManualPathEditing(PickItemResult pickResult)
|
||||
{
|
||||
// 检查当前选中的通道状态
|
||||
LogManager.Debug($"[手动编辑] 当前_selectedChannels状态: {(_walkableAreas == null ? "NULL" : $"包含{_walkableAreas.Count}个项目")}");
|
||||
|
||||
// 如果没有选中的通道,尝试实时搜索
|
||||
if (_walkableAreas == null || _walkableAreas.Count == 0)
|
||||
try
|
||||
{
|
||||
LogManager.Debug("[手动编辑] 没有预选通道,开始实时搜索可通行的物流模型");
|
||||
SearchAndSetTraversableChannels();
|
||||
}
|
||||
|
||||
// 检查是否在可通行的物流模型内并处理点击
|
||||
if (_walkableAreas != null && _walkableAreas.Any())
|
||||
{
|
||||
bool isInTraversableLogisticsModel = IsItemInSelectedChannels(pickResult.ModelItem) ||
|
||||
IsItemChildOfSelectedChannels(pickResult.ModelItem);
|
||||
|
||||
LogManager.Debug($"[手动编辑] 在可通行的物流模型内: {isInTraversableLogisticsModel}");
|
||||
|
||||
if (isInTraversableLogisticsModel)
|
||||
// 如果启用了空轨吸附,先吸附到基准路径
|
||||
Point3D clickedPoint = pickResult.Point;
|
||||
if (_enableRailSnapping)
|
||||
{
|
||||
// 手动路径编辑 - 根据当前模式处理点击
|
||||
if (PathEditState == PathEditState.AddingPoints)
|
||||
{
|
||||
// 添加路径点模式 - 使用预览点
|
||||
LogManager.Debug("[手动编辑] 设置预览点位置");
|
||||
var previewPoint = SetPreviewPoint(pickResult.Point);
|
||||
clickedPoint = SnapToRailBaseline(clickedPoint);
|
||||
}
|
||||
|
||||
if (previewPoint != null)
|
||||
{
|
||||
LogManager.Debug($"[手动编辑] ✓ 预览点已设置: {previewPoint.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning("[手动编辑] ✗ 预览点设置失败");
|
||||
}
|
||||
}
|
||||
else if (PathEditState == PathEditState.EditingPoint)
|
||||
// 检查当前选中的通道状态
|
||||
LogManager.Debug($"[手动编辑] 当前_selectedChannels状态: {(_walkableAreas == null ? "NULL" : $"包含{_walkableAreas.Count}个项目")}");
|
||||
|
||||
// 如果没有选中的通道,尝试实时搜索
|
||||
if (_walkableAreas == null || _walkableAreas.Count == 0)
|
||||
{
|
||||
LogManager.Debug("[手动编辑] 没有预选通道,开始实时搜索可通行的物流模型");
|
||||
SearchAndSetTraversableChannels();
|
||||
}
|
||||
|
||||
// 空轨路径直接添加路径点,不检查可通行物流模型
|
||||
if (_enableRailSnapping)
|
||||
{
|
||||
LogManager.Debug("[手动编辑] 空轨路径模式,直接添加路径点");
|
||||
var pathPoint = AddPathPointIn3D(clickedPoint);
|
||||
if (pathPoint != null)
|
||||
{
|
||||
// 修改路径点模式 - 设置预览位置
|
||||
LogManager.Debug("[手动编辑] 设置修改路径点预览位置");
|
||||
SetEditingPreviewPoint(pickResult.Point);
|
||||
LogManager.Debug($"[手动编辑] ✓ 修改路径点预览位置已设置: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
|
||||
LogManager.Debug($"[手动编辑] ✓ 空轨路径点添加成功: {pathPoint.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 其他编辑模式 - 保持原有逻辑
|
||||
LogManager.Debug("[手动编辑] 调用AddPathPointIn3D添加路径点");
|
||||
var pathPoint = AddPathPointIn3D(pickResult.Point);
|
||||
LogManager.Warning("[手动编辑] ✗ 空轨路径点添加失败");
|
||||
}
|
||||
}
|
||||
// 地面路径检查是否在可通行的物流模型内并处理点击
|
||||
else if (_walkableAreas != null && _walkableAreas.Any())
|
||||
{
|
||||
bool isInTraversableLogisticsModel = IsItemInSelectedChannels(pickResult.ModelItem) ||
|
||||
IsItemChildOfSelectedChannels(pickResult.ModelItem);
|
||||
|
||||
if (pathPoint != null)
|
||||
LogManager.Debug($"[手动编辑] 在可通行的物流模型内: {isInTraversableLogisticsModel}");
|
||||
|
||||
if (isInTraversableLogisticsModel)
|
||||
{
|
||||
// 手动路径编辑 - 根据当前模式处理点击
|
||||
if (PathEditState == PathEditState.AddingPoints)
|
||||
{
|
||||
LogManager.Debug($"[手动编辑] ✓ 路径点添加成功: {pathPoint.Name}");
|
||||
// 添加路径点模式 - 使用预览点
|
||||
LogManager.Debug("[手动编辑] 设置预览点位置");
|
||||
var previewPoint = SetPreviewPoint(clickedPoint);
|
||||
|
||||
if (previewPoint != null)
|
||||
{
|
||||
LogManager.Debug($"[手动编辑] ✓ 预览点已设置: {previewPoint.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning("[手动编辑] ✗ 预览点设置失败");
|
||||
}
|
||||
}
|
||||
else if (PathEditState == PathEditState.EditingPoint)
|
||||
{
|
||||
// 修改路径点模式 - 设置预览位置
|
||||
LogManager.Debug("[手动编辑] 设置修改路径点预览位置");
|
||||
SetEditingPreviewPoint(clickedPoint);
|
||||
LogManager.Debug($"[手动编辑] ✓ 修改路径点预览位置已设置: ({clickedPoint.X:F3}, {clickedPoint.Y:F3}, {clickedPoint.Z:F3})");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning("[手动编辑] ✗ 路径点添加失败");
|
||||
// 其他编辑模式 - 保持原有逻辑
|
||||
LogManager.Debug("[手动编辑] 调用AddPathPointIn3D添加路径点");
|
||||
var pathPoint = AddPathPointIn3D(clickedPoint);
|
||||
|
||||
if (pathPoint != null)
|
||||
{
|
||||
LogManager.Debug($"[手动编辑] ✓ 路径点添加成功: {pathPoint.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning("[手动编辑] ✗ 路径点添加失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug("[手动编辑] ✗ 点击位置不在可通行的物流模型内");
|
||||
RaiseErrorOccurred("点击位置不在物流通道内,请选择有效的物流路径位置");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug("[手动编辑] ✗ 点击位置不在可通行的物流模型内");
|
||||
RaiseErrorOccurred("点击位置不在物流通道内,请选择有效的物流路径位置");
|
||||
LogManager.Warning("[手动编辑] ✗ 未找到可通行的物流模型");
|
||||
RaiseErrorOccurred("未找到可通行的物流通道,请先选择或配置物流通道");
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning("[手动编辑] ✗ 未找到可通行的物流模型");
|
||||
RaiseErrorOccurred("未找到可通行的物流通道,请先选择或配置物流通道");
|
||||
LogManager.Error($"[手动编辑] 处理异常: {ex.Message}");
|
||||
LogManager.Error($"[手动编辑] 堆栈: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将点击点吸附到空轨基准路径
|
||||
/// </summary>
|
||||
private Point3D SnapToRailBaseline(Point3D clickedPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取所有空轨基准路径
|
||||
var baselinePaths = PathPointRenderPlugin.Instance?.GetAllRailBaselinePaths();
|
||||
if (baselinePaths == null || baselinePaths.Count == 0)
|
||||
{
|
||||
LogManager.Warning("[空轨吸附] 没有找到空轨基准路径,不进行吸附");
|
||||
return clickedPoint;
|
||||
}
|
||||
|
||||
// 找到最近的基准路径点
|
||||
Point3D nearestPoint = null;
|
||||
double minDistance = double.MaxValue;
|
||||
double maxSnapDistance = 2.0; // 最大吸附距离(模型单位)
|
||||
|
||||
foreach (var pathPoints in baselinePaths.Values)
|
||||
{
|
||||
foreach (var point in pathPoints)
|
||||
{
|
||||
double distance = CalculateDistance3D(clickedPoint, point);
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
nearestPoint = point;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nearestPoint != null && minDistance <= maxSnapDistance)
|
||||
{
|
||||
LogManager.Info($"[空轨吸附] 点击点 ({clickedPoint.X:F2}, {clickedPoint.Y:F2}, {clickedPoint.Z:F2}) 吸附到基准路径点 ({nearestPoint.X:F2}, {nearestPoint.Y:F2}, {nearestPoint.Z:F2}),距离 {minDistance:F2}");
|
||||
return nearestPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning($"[空轨吸附] 未找到合适的基准路径点(最近距离: {minDistance:F2},最大吸附距离: {maxSnapDistance})");
|
||||
return clickedPoint;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[空轨吸附] 吸附失败: {ex.Message}");
|
||||
return clickedPoint;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算3D距离
|
||||
/// </summary>
|
||||
private double CalculateDistance3D(Point3D a, Point3D b)
|
||||
{
|
||||
double dx = a.X - b.X;
|
||||
double dy = a.Y - b.Y;
|
||||
double dz = a.Z - b.Z;
|
||||
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 搜索并设置可通行的通道
|
||||
/// </summary>
|
||||
@ -2963,6 +3077,16 @@ namespace NavisworksTransport
|
||||
|
||||
try
|
||||
{
|
||||
// 空轨路径不进行曲线化,直接保存
|
||||
if (route.PathType == PathType.Rail)
|
||||
{
|
||||
LogManager.Info($"空轨路径跳过曲线化,直接保存: {route.Name}");
|
||||
SavePathToDatabase(route);
|
||||
LogManager.Info($"空轨路径已保存到数据库: {route.Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 地面路径进行曲线化
|
||||
double samplingStep = ConfigManager.Instance.Current.PathEditing.ArcSamplingStep;
|
||||
route.TurnRadius = ConfigManager.Instance.Current.PathEditing.DefaultPathTurnRadius;
|
||||
List<string> warnings;
|
||||
|
||||
@ -44,7 +44,23 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
EditingPoint
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 路径类型枚举
|
||||
/// </summary>
|
||||
public enum PathType
|
||||
{
|
||||
/// <summary>
|
||||
/// 地面路径 - 车辆在地面运行
|
||||
/// </summary>
|
||||
Ground = 0,
|
||||
|
||||
/// <summary>
|
||||
/// 空轨路径 - 车辆悬挂在空轨下方运行
|
||||
/// </summary>
|
||||
Rail = 1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通道检测结果
|
||||
/// </summary>
|
||||
@ -546,6 +562,11 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
public bool IsCurved { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 路径类型
|
||||
/// </summary>
|
||||
public PathType PathType { get; set; } = PathType.Ground;
|
||||
|
||||
// 数据库分析相关属性
|
||||
/// <summary>
|
||||
/// 碰撞数量(从数据库加载)
|
||||
|
||||
@ -118,7 +118,12 @@ namespace NavisworksTransport
|
||||
/// <summary>
|
||||
/// 安全警告样式(红色)
|
||||
/// </summary>
|
||||
SafetyWarning
|
||||
SafetyWarning,
|
||||
|
||||
/// <summary>
|
||||
/// 空轨基准路径样式(浅红色)
|
||||
/// </summary>
|
||||
RailBaseline
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -367,6 +372,9 @@ namespace NavisworksTransport
|
||||
// 预览连线标记
|
||||
private List<LineMarker> _previewLines = new List<LineMarker>();
|
||||
|
||||
// 空轨基准路径可视化
|
||||
private Dictionary<string, PathVisualization> _railBaselineVisualizations = new Dictionary<string, PathVisualization>();
|
||||
|
||||
// 当前网格大小(米),用于自适应点大小计算
|
||||
private double _currentGridSizeInMeters;
|
||||
|
||||
@ -495,14 +503,16 @@ namespace NavisworksTransport
|
||||
|
||||
// 检查是否有路径或预览点需要渲染
|
||||
int pathCount;
|
||||
int railBaselineCount;
|
||||
bool hasPreviewPoint;
|
||||
lock (_lockObject)
|
||||
{
|
||||
pathCount = _pathVisualizations.Count;
|
||||
railBaselineCount = _railBaselineVisualizations.Count;
|
||||
hasPreviewPoint = _previewMarker != null;
|
||||
}
|
||||
|
||||
if (pathCount == 0 && !hasPreviewPoint)
|
||||
if (pathCount == 0 && railBaselineCount == 0 && !hasPreviewPoint)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -591,6 +601,19 @@ namespace NavisworksTransport
|
||||
graphics.Cylinder(previewLine.StartPoint, previewLine.EndPoint, previewLine.Radius);
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染空轨基准路径(浅红色)
|
||||
if (_railBaselineVisualizations.Count > 0)
|
||||
{
|
||||
foreach (var visualization in _railBaselineVisualizations.Values)
|
||||
{
|
||||
foreach (var pathLineMarker in visualization.PathLineMarkers)
|
||||
{
|
||||
graphics.Color(pathLineMarker.Color, 0.7); // 使用70%透明度
|
||||
graphics.Cylinder(pathLineMarker.StartPoint, pathLineMarker.EndPoint, pathLineMarker.Radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
graphics.EndModelContext();
|
||||
@ -854,6 +877,129 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 渲染空轨基准路径
|
||||
/// </summary>
|
||||
/// <param name="railModelId">空轨模型ID</param>
|
||||
/// <param name="baselinePoints">基准路径点列表</param>
|
||||
public void RenderRailBaseline(string railModelId, List<Point3D> baselinePoints)
|
||||
{
|
||||
if (string.IsNullOrEmpty(railModelId) || baselinePoints == null || baselinePoints.Count < 2)
|
||||
{
|
||||
LogManager.Warning($"[路径渲染] RenderRailBaseline参数无效: railModelId={railModelId}, points={(baselinePoints?.Count ?? 0)}");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LogManager.Info($"[路径渲染] 开始渲染空轨基准路径: {railModelId}, {baselinePoints.Count} 个点");
|
||||
|
||||
// 先清除该模型的旧基准路径可视化
|
||||
ClearRailBaseline(railModelId);
|
||||
|
||||
// 创建PathVisualization对象
|
||||
var visualization = new PathVisualization
|
||||
{
|
||||
PathId = railModelId,
|
||||
ShowControlVisualization = false, // 不显示控制点
|
||||
LastUpdated = DateTime.Now
|
||||
};
|
||||
|
||||
var renderStyle = GetRenderStyle(RenderStyleName.RailBaseline);
|
||||
LogManager.Info($"[路径渲染] RailBaseline样式: R={renderStyle.Color.R}, G={renderStyle.Color.G}, B={renderStyle.Color.B}, Alpha={renderStyle.Alpha}");
|
||||
|
||||
// 创建连线标记
|
||||
for (int i = 0; i < baselinePoints.Count - 1; i++)
|
||||
{
|
||||
var start = baselinePoints[i];
|
||||
var end = baselinePoints[i + 1];
|
||||
|
||||
var lineMarker = new LineMarker
|
||||
{
|
||||
StartPoint = start,
|
||||
EndPoint = end,
|
||||
Color = renderStyle.Color,
|
||||
Radius = GetLineRadius() * 0.2,
|
||||
SegmentType = PathSegmentType.Straight,
|
||||
FromIndex = i,
|
||||
ToIndex = i + 1
|
||||
};
|
||||
|
||||
visualization.PathLineMarkers.Add(lineMarker);
|
||||
}
|
||||
|
||||
LogManager.Info($"[路径渲染] 创建了 {visualization.PathLineMarkers.Count} 个连线标记");
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
_railBaselineVisualizations[railModelId] = visualization;
|
||||
LogManager.Info($"[路径渲染] 已添加到字典,当前共有 {_railBaselineVisualizations.Count} 个基准路径");
|
||||
}
|
||||
|
||||
RequestViewRefresh();
|
||||
LogManager.Info($"[路径渲染] 已请求视图刷新");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[路径渲染] 渲染空轨基准路径失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除指定空轨的基准路径可视化
|
||||
/// </summary>
|
||||
/// <param name="railModelId">空轨模型ID</param>
|
||||
public void ClearRailBaseline(string railModelId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(railModelId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_railBaselineVisualizations.Remove(railModelId))
|
||||
{
|
||||
RequestViewRefresh();
|
||||
LogManager.Info($"[路径渲染] 已清除空轨基准路径: {railModelId}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有空轨基准路径
|
||||
/// </summary>
|
||||
/// <returns>所有空轨基准路径的字典</returns>
|
||||
public Dictionary<string, List<Point3D>> GetAllRailBaselinePaths()
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
var result = new Dictionary<string, List<Point3D>>();
|
||||
foreach (var kvp in _railBaselineVisualizations)
|
||||
{
|
||||
result[kvp.Key] = kvp.Value.PathLineMarkers.Select(m => m.StartPoint).ToList();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有空轨基准路径可视化
|
||||
/// </summary>
|
||||
public void ClearAllRailBaselines()
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
int count = _railBaselineVisualizations.Count;
|
||||
_railBaselineVisualizations.Clear();
|
||||
if (count > 0)
|
||||
{
|
||||
RequestViewRefresh();
|
||||
LogManager.Info($"[路径渲染] 已清除所有空轨基准路径, 共 {count} 个");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空所有路径
|
||||
/// </summary>
|
||||
@ -942,9 +1088,30 @@ namespace NavisworksTransport
|
||||
// 构建控制点连线(用户意图,半透明)
|
||||
BuildControlLines(visualization, sortedPoints);
|
||||
|
||||
// 所有模式都使用曲线化后的路径(Edges)
|
||||
if (visualization.PathRoute.Edges != null && visualization.PathRoute.Edges.Count > 0)
|
||||
// 地面路径使用曲线化后的路径(Edges)
|
||||
// 空轨路径直接使用控制点连线作为路径连线
|
||||
if (visualization.PathRoute.PathType == NavisworksTransport.PathType.Rail)
|
||||
{
|
||||
// 空轨路径:将控制点连线复制到路径连线,使用不透明样式
|
||||
foreach (var controlLine in visualization.ControlLineMarkers)
|
||||
{
|
||||
var pathLineMarker = new LineMarker
|
||||
{
|
||||
StartPoint = controlLine.StartPoint,
|
||||
EndPoint = controlLine.EndPoint,
|
||||
Color = GetRenderStyle(RenderStyleName.Line).Color,
|
||||
Radius = GetLineRadius(),
|
||||
SegmentType = PathSegmentType.Straight,
|
||||
FromIndex = controlLine.FromIndex,
|
||||
ToIndex = controlLine.ToIndex
|
||||
};
|
||||
visualization.PathLineMarkers.Add(pathLineMarker);
|
||||
}
|
||||
LogManager.Debug($"[路径渲染] 空轨路径使用控制点连线,共 {visualization.PathLineMarkers.Count} 条");
|
||||
}
|
||||
else if (visualization.PathRoute.Edges != null && visualization.PathRoute.Edges.Count > 0)
|
||||
{
|
||||
// 地面路径:使用曲线化后的路径
|
||||
BuildPathLines(visualization, sortedPoints);
|
||||
}
|
||||
|
||||
@ -1371,6 +1538,9 @@ namespace NavisworksTransport
|
||||
case RenderStyleName.SafetyWarning:
|
||||
return new RenderStyle(Color.FromByteRGB(244, 67, 54), 0.85); // Material Red安全警告,15%透明
|
||||
|
||||
case RenderStyleName.RailBaseline:
|
||||
return new RenderStyle(Color.FromByteRGB(255, 138, 128), 0.7); // 浅红色,30%透明
|
||||
|
||||
default:
|
||||
return new RenderStyle(Color.White, 1.0); // 默认白色,完全不透明
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using NavisworksTransport.Utils;
|
||||
using NavisworksTransport.PathPlanning;
|
||||
|
||||
namespace NavisworksTransport
|
||||
{
|
||||
@ -88,10 +89,15 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
停车位 = 9,
|
||||
|
||||
/// <summary>
|
||||
/// 空轨 - 空中运输路径,车辆悬挂运行,权重0.7
|
||||
/// </summary>
|
||||
空轨 = 10,
|
||||
|
||||
/// <summary>
|
||||
/// 无关项 - 不参与网格生成的大型构件(如地基、结构基础等)
|
||||
/// </summary>
|
||||
无关项 = 10
|
||||
无关项 = 11
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -170,6 +176,13 @@ namespace NavisworksTransport
|
||||
propertyInternalNames);
|
||||
|
||||
LogManager.Info($"[属性添加] 添加操作完成,成功添加 {successCount} 个模型的属性");
|
||||
|
||||
// 如果是空轨类型,预计算基准路径
|
||||
if (elementType == LogisticsElementType.空轨 && successCount > 0)
|
||||
{
|
||||
PreCalculateRailBaselinePaths(items);
|
||||
}
|
||||
|
||||
return successCount;
|
||||
}
|
||||
|
||||
@ -524,6 +537,52 @@ namespace NavisworksTransport
|
||||
return LogisticsElementType.障碍物;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 预计算空轨基准路径
|
||||
/// </summary>
|
||||
/// <param name="items">已设置为空轨的模型项集合</param>
|
||||
private static void PreCalculateRailBaselinePaths(ModelItemCollection items)
|
||||
{
|
||||
if (items == null || items.Count == 0)
|
||||
return;
|
||||
|
||||
LogManager.Info($"[空轨] 开始预计算 {items.Count} 个空轨模型的基准路径");
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
foreach (ModelItem item in items)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 调用RailGeometryHelper提取基准路径
|
||||
var baselinePath = PathPlanning.RailGeometryHelper.ExtractRailBaselinePath(item);
|
||||
|
||||
if (baselinePath != null && baselinePath.PathPoints != null && baselinePath.PathPoints.Count > 0)
|
||||
{
|
||||
successCount++;
|
||||
LogManager.Info($"[空轨] 成功提取模型 {item.DisplayName} 的基准路径,共 {baselinePath.PathPoints.Count} 个点,总长度 {baselinePath.TotalLength:F2} 模型单位");
|
||||
|
||||
// 自动可视化基准路径
|
||||
var railModelId = $"{item.DisplayName}_{item.GetHashCode()}";
|
||||
PathPointRenderPlugin.Instance.RenderRailBaseline(railModelId, baselinePath.PathPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
failCount++;
|
||||
LogManager.Warning($"[空轨] 模型 {item.DisplayName} 的基准路径提取失败或路径为空");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
failCount++;
|
||||
LogManager.Error($"[空轨] 预计算模型 {item.DisplayName} 的基准路径时发生错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[空轨] 基准路径预计算完成,成功 {successCount} 个,失败 {failCount} 个");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除选定模型项的物流属性
|
||||
/// </summary>
|
||||
|
||||
@ -2362,7 +2362,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
};
|
||||
|
||||
// 检查路径类型
|
||||
if (astarPath.Type == PathType.ClosestApproach)
|
||||
if (astarPath.Type == Roy_T.AStar.Paths.PathType.ClosestApproach)
|
||||
{
|
||||
LogManager.Info($"[A*执行] 找到部分路径(最近接近),包含 {astarPath.Edges.Count + 1} 个网格点");
|
||||
LogManager.Warning($"[A*执行] 无法完全到达目标点,已找到最接近的可达点");
|
||||
|
||||
1153
src/PathPlanning/RailGeometryHelper.cs
Normal file
1153
src/PathPlanning/RailGeometryHelper.cs
Normal file
File diff suppressed because it is too large
Load Diff
34
src/UI/WPF/Converters/PathTypeConverter.cs
Normal file
34
src/UI/WPF/Converters/PathTypeConverter.cs
Normal file
@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// 路径类型转换器,将PathType枚举转换为中文显示
|
||||
/// </summary>
|
||||
public class PathTypeConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is NavisworksTransport.PathType pathType)
|
||||
{
|
||||
switch (pathType)
|
||||
{
|
||||
case NavisworksTransport.PathType.Ground:
|
||||
return "地面";
|
||||
case NavisworksTransport.PathType.Rail:
|
||||
return "空轨";
|
||||
default:
|
||||
return "未知";
|
||||
}
|
||||
}
|
||||
return "地面";
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,6 +182,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
private double _totalLength;
|
||||
private bool _isValidated;
|
||||
private string _validationStatus;
|
||||
private NavisworksTransport.PathType _pathType;
|
||||
|
||||
// UI状态管理
|
||||
private readonly UIStateManager _uiStateManager;
|
||||
@ -272,6 +273,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
set => SetProperty(ref _isOptimal, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 路径类型
|
||||
/// </summary>
|
||||
public NavisworksTransport.PathType PathType
|
||||
{
|
||||
get => _pathType;
|
||||
set => SetProperty(ref _pathType, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
|
||||
@ -246,10 +246,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
LogManager.Warning($"[路径可视化] 未找到对应的Core路径: {_selectedPathRoute.Name}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug("[路径可视化] 没有选中的路径,仅清理显示");
|
||||
}
|
||||
|
||||
// 3. 恢复网格可视化(如果网格可视化已启用且当前路径有关联的GridMap)
|
||||
if (_pathPlanningManager?.IsAnyGridVisualizationEnabled == true)
|
||||
@ -638,6 +634,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
#region 命令
|
||||
|
||||
public ICommand NewPathCommand { get; private set; }
|
||||
public ICommand NewRailPathCommand { get; private set; }
|
||||
public ICommand DeletePathCommand { get; private set; }
|
||||
public ICommand RenamePathCommand { get; private set; }
|
||||
public ICommand StartEditCommand { get; private set; }
|
||||
@ -665,6 +662,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
public bool CanExecuteNewPath => !IsSelectingStartPoint && !IsSelectingEndPoint;
|
||||
|
||||
public bool CanExecuteNewRailPath => !IsSelectingStartPoint && !IsSelectingEndPoint;
|
||||
|
||||
public bool CanExecuteStartEdit => SelectedPathRoute != null &&
|
||||
(_pathPlanningManager?.PathEditState == PathEditState.Viewing);
|
||||
|
||||
@ -802,6 +801,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
private void InitializeCommands()
|
||||
{
|
||||
NewPathCommand = new RelayCommand(async () => await ExecuteNewPathAsync(), () => CanExecuteNewPath);
|
||||
NewRailPathCommand = new RelayCommand(async () => await ExecuteNewRailPathAsync(), () => CanExecuteNewRailPath);
|
||||
DeletePathCommand = new RelayCommand(async () => await ExecuteDeletePathAsync());
|
||||
RenamePathCommand = new RelayCommand(async () => await ExecuteRenamePathAsync());
|
||||
StartEditCommand = new RelayCommand(async () => await ExecuteAddPathPointAsync(), () => CanExecuteStartEdit);
|
||||
@ -910,6 +910,97 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}, "新建路径");
|
||||
}
|
||||
|
||||
private async Task ExecuteNewRailPathAsync()
|
||||
{
|
||||
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)
|
||||
{
|
||||
var newRoute = _pathPlanningManager.StartCreatingNewRoute(isRailPath: true);
|
||||
|
||||
if (newRoute != null)
|
||||
{
|
||||
// 设置路径类型为空轨
|
||||
newRoute.PathType = PathType.Rail;
|
||||
|
||||
// 创建对应的 WPF ViewModel
|
||||
var newPathViewModel = new PathRouteViewModel
|
||||
{
|
||||
Id = newRoute.Id,
|
||||
Name = newRoute.Name,
|
||||
Description = newRoute.Description,
|
||||
IsActive = true,
|
||||
PathType = PathType.Rail
|
||||
};
|
||||
|
||||
// 转换路径点
|
||||
foreach (var corePoint in newRoute.Points)
|
||||
{
|
||||
var wpfPoint = new PathPointViewModel
|
||||
{
|
||||
Id = corePoint.Id,
|
||||
Name = corePoint.Name,
|
||||
X = corePoint.X,
|
||||
Y = corePoint.Y,
|
||||
Z = corePoint.Z,
|
||||
Type = corePoint.Type
|
||||
};
|
||||
newPathViewModel.Points.Add(wpfPoint);
|
||||
}
|
||||
|
||||
// 添加到 UI 列表并选中
|
||||
PathRoutes.Add(newPathViewModel);
|
||||
SelectedPathRoute = newPathViewModel;
|
||||
|
||||
// 强制重新初始化ToolPlugin以确保获得鼠标焦点
|
||||
if (!ForceReinitializeToolPlugin(subscribeToEvents: true))
|
||||
{
|
||||
UpdateMainStatus("ToolPlugin初始化失败,请重试");
|
||||
LogManager.Error("新建空轨路径:ToolPlugin初始化失败");
|
||||
return;
|
||||
}
|
||||
|
||||
// 启动点击工具,设置空轨吸附模式
|
||||
_pathPlanningManager.StartClickTool(PathPointType.WayPoint, enableRailSnapping: true);
|
||||
LogManager.Info($"已启动空轨路径点击工具(吸附模式): {newRoute.Name}");
|
||||
|
||||
UpdateMainStatus($"已进入新建空轨路径模式: {newRoute.Name} - 请在3D视图中点击空轨模型设置路径点(将自动吸附到基准路径)");
|
||||
LogManager.Info($"开始新建空轨路径: {newRoute.Name},已强制重新初始化ToolPlugin获取鼠标焦点");
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateMainStatus("创建新空轨路径失败:没有可通行的物流模型");
|
||||
LogManager.Error("创建新空轨路径失败:没有可通行的物流模型");
|
||||
MessageBox.Show("创建新空轨路径失败:没有找到任何可通行的物流模型。\n请先为模型设置可通行的物流属性,然后再尝试创建路径。", "错误",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateMainStatus("路径规划管理器未初始化");
|
||||
LogManager.Error("路径规划管理器未初始化");
|
||||
}
|
||||
}, "新建空轨路径");
|
||||
}
|
||||
|
||||
private async Task ExecuteDeletePathAsync()
|
||||
{
|
||||
if (SelectedPathRoute == null) return;
|
||||
@ -1366,13 +1457,24 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
SelectedPathRoute.IsActive = true;
|
||||
UpdateMainStatus($"正在激活3D路径编辑模式: {SelectedPathRoute.Name}...");
|
||||
|
||||
// 检查是否是空轨路径
|
||||
bool isRailPath = SelectedPathRoute.PathType == PathType.Rail;
|
||||
|
||||
// 启动PathPlanningManager的点击工具,这会设置正确的编辑状态
|
||||
if (_pathPlanningManager != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathPlanningManager.StartClickTool(PathPointType.WayPoint);
|
||||
LogManager.Info($"已启动PathPlanningManager点击工具: {SelectedPathRoute.Name}");
|
||||
if (isRailPath)
|
||||
{
|
||||
_pathPlanningManager.StartClickTool(PathPointType.WayPoint, enableRailSnapping: true);
|
||||
LogManager.Info($"已启动PathPlanningManager点击工具(空轨吸附模式): {SelectedPathRoute.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_pathPlanningManager.StartClickTool(PathPointType.WayPoint);
|
||||
LogManager.Info($"已启动PathPlanningManager点击工具: {SelectedPathRoute.Name}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1390,7 +1492,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateMainStatus($"已进入3D路径编辑模式: {SelectedPathRoute.Name} - 在3D视图中点击设置路径点");
|
||||
string modeText = isRailPath ? "(空轨吸附模式)" : "";
|
||||
UpdateMainStatus($"已进入3D路径编辑模式: {SelectedPathRoute.Name} {modeText} - 在3D视图中点击设置路径点");
|
||||
LogManager.Info($"开始添加路径点: {SelectedPathRoute.Name},已启动点击工具");
|
||||
|
||||
// 手动触发按钮状态更新
|
||||
@ -3023,7 +3126,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
Id = coreRoute.Id,
|
||||
Name = coreRoute.Name,
|
||||
Description = coreRoute.Description,
|
||||
IsActive = false // 历史路径默认不激活
|
||||
IsActive = false, // 历史路径默认不激活
|
||||
PathType = coreRoute.PathType
|
||||
};
|
||||
|
||||
// 设置时间信息
|
||||
|
||||
@ -26,6 +26,7 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
|
||||
|
||||
<!-- 转换器资源 -->
|
||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisConverter"/>
|
||||
<converters:PathTypeConverter x:Key="PathTypeConverter"/>
|
||||
|
||||
<!-- 滑块样式 -->
|
||||
<Style x:Key="SliderStyle" TargetType="Slider">
|
||||
@ -211,6 +212,10 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
|
||||
<Button Content="手动创建"
|
||||
Command="{Binding NewPathCommand}"
|
||||
Style="{StaticResource ActionButtonStyle}"/>
|
||||
<Button Content="空轨路径"
|
||||
Command="{Binding NewRailPathCommand}"
|
||||
Style="{StaticResource ActionButtonStyle}"
|
||||
Margin="5,0,0,0"/>
|
||||
<Button Content="删除"
|
||||
Command="{Binding DeletePathCommand}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
@ -236,7 +241,8 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
|
||||
BorderThickness="1">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Header="路径名称" DisplayMemberBinding="{Binding Name}" Width="160"/>
|
||||
<GridViewColumn Header="路径名称" DisplayMemberBinding="{Binding Name}" Width="120"/>
|
||||
<GridViewColumn Header="类型" DisplayMemberBinding="{Binding PathType, Converter={StaticResource PathTypeConverter}}" Width="50"/>
|
||||
<GridViewColumn Header="点数" DisplayMemberBinding="{Binding PointCount}" Width="50"/>
|
||||
<GridViewColumn Header="状态" DisplayMemberBinding="{Binding StatusString}" Width="60"/>
|
||||
<GridViewColumn Header="创建时间" DisplayMemberBinding="{Binding CreatedTime, StringFormat='MM-dd HH:mm:ss'}" Width="100"/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user