优化路径分析功能;把资源文件集中到resources目录

This commit is contained in:
tian 2026-02-14 20:52:58 +08:00
parent 77b9da40fc
commit 3138b73a5f
13 changed files with 561 additions and 186 deletions

View File

@ -275,6 +275,7 @@
<Compile Include="src\UI\WPF\ViewModels\PathAnalysisViewModel.cs" /> <Compile Include="src\UI\WPF\ViewModels\PathAnalysisViewModel.cs" />
<Compile Include="src\UI\WPF\ViewModels\TimeTagViewModel.cs" /> <Compile Include="src\UI\WPF\ViewModels\TimeTagViewModel.cs" />
<Compile Include="src\UI\WPF\ViewModels\BatchTaskManagementViewModel.cs" /> <Compile Include="src\UI\WPF\ViewModels\BatchTaskManagementViewModel.cs" />
<!-- UI - WPF Helpers -->
<!-- UI - WPF Collections --> <!-- UI - WPF Collections -->
<Compile Include="src\UI\WPF\Collections\ThreadSafeObservableCollection.cs" /> <Compile Include="src\UI\WPF\Collections\ThreadSafeObservableCollection.cs" />
<!-- UI - WPF Interfaces --> <!-- UI - WPF Interfaces -->
@ -448,18 +449,25 @@
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WinFX\3.0\Microsoft.WinFX.targets" Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\WinFX\3.0\Microsoft.WinFX.targets')" /> <Import Project="$(MSBuildExtensionsPath)\Microsoft\WinFX\3.0\Microsoft.WinFX.targets" Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\WinFX\3.0\Microsoft.WinFX.targets')" />
<ItemGroup> <ItemGroup>
<!-- Localization Files --> <!-- Localization Files -->
<None Include="src\Resources\NavisworksTransportPlugin.name.txt"> <None Include="resources\TransportPlugin.name.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>NavisworksTransportPlugin.name.txt</Link> <Link>TransportPlugin.name.txt</Link>
</None> </None>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- Import NETStandard.Library targets to support .NET Standard 2.0 libraries --> <!-- Import NETStandard.Library targets to support .NET Standard 2.0 libraries -->
<Import Project="packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets')" /> <Import Project="packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets')" />
<ItemGroup> <ItemGroup>
<!-- Configuration Template File --> <!-- Configuration Template File (in resources folder) -->
<None Include="default_config.toml"> <None Include="resources\default_config.toml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>resources\default_config.toml</Link>
</None>
<!-- Plugin Name File (in resources folder) -->
<None Include="resources\TransportPlugin.name.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>resources\TransportPlugin.name.txt</Link>
</None> </None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -12,7 +12,12 @@ set "TARGET_DIR=C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\Naviswork
if not exist "%TARGET_DIR%" mkdir "%TARGET_DIR%" if not exist "%TARGET_DIR%" mkdir "%TARGET_DIR%"
copy "bin\x64\Release\NavisworksTransportPlugin.dll" "%TARGET_DIR%\" >nul copy "bin\x64\Release\NavisworksTransportPlugin.dll" "%TARGET_DIR%\" >nul
if exist "bin\x64\Release\NavisworksTransportPlugin.name.txt" copy "bin\x64\Release\NavisworksTransportPlugin.name.txt" "%TARGET_DIR%\" >nul
if exist "bin\x64\Release\default_config.toml" copy "bin\x64\Release\default_config.toml" "%TARGET_DIR%\" >nul :: 复制resources文件夹包含chart.js等本地资源
if exist "bin\x64\Release\resources" (
if not exist "%TARGET_DIR%\resources" mkdir "%TARGET_DIR%\resources"
copy "bin\x64\Release\resources\*" "%TARGET_DIR%\resources\" >nul
echo Resources folder deployed.
)
echo Plugin deployed successfully! echo Plugin deployed successfully!

View File

@ -2,6 +2,10 @@
## 功能点 ## 功能点
### [2026/2/14]
1. [x] (优化)实现完整的路径分析功能
### [2026/2/8] ### [2026/2/8]
1. [x] (功能)增加预计算结果分析和排除建议 1. [x] (功能)增加预计算结果分析和排除建议

View File

@ -9,6 +9,7 @@
## 一、项目背景与目标 ## 一、项目背景与目标
### 1.1 背景 ### 1.1 背景
- **应用场景**: 大型装置内进行精密组件物流仿真 - **应用场景**: 大型装置内进行精密组件物流仿真
- **核心目标**: 确保组件能安全到达安装位置(终点) - **核心目标**: 确保组件能安全到达安装位置(终点)
- **最高优先级**: 减少碰撞(安全性) - **最高优先级**: 减少碰撞(安全性)
@ -16,6 +17,7 @@
- **不涉及**: 经济成本 - **不涉及**: 经济成本
### 1.2 设计原则 ### 1.2 设计原则
1. **实用优先**: 指标不在于多,而在于有用 1. **实用优先**: 指标不在于多,而在于有用
2. **聚焦终点**: 核心目标是"找出到同一安装位置的最佳路径" 2. **聚焦终点**: 核心目标是"找出到同一安装位置的最佳路径"
3. **可解释性**: 每个指标和评分都要有明确的物理意义 3. **可解释性**: 每个指标和评分都要有明确的物理意义
@ -27,11 +29,11 @@
### 2.1 三种分析策略 ### 2.1 三种分析策略
| 策略 | 权重配置(安全/效率/转弯/曲折/冗余 | 适用场景 | | 策略 | 权重配置(安全/效率/转弯/直达 | 适用场景 |
|-----|-----------------------------------|---------| |-----|--------------------------------|---------|
| **安全优先** | 50% / 20% / 10% / 10% / 10% | 精密/易损组件,首次运输 | | **安全优先** | 50% / 30% / 10% / 10% | 精密/易损组件,首次运输 |
| **效率优先** | 20% / 40% / 15% / 15% / 10% | 紧急任务,多次往返 | | **效率优先** | 20% / 50% / 15% / 15% | 紧急任务,多次往返 |
| **平衡模式** | 35% / 30% / 10% / 15% / 10% | 通用场景(默认) | | **平衡模式** | 35% / 35% / 15% / 15% | 通用场景(默认) |
### 2.2 权重配置代码 ### 2.2 权重配置代码
@ -42,19 +44,19 @@ public static class AnalysisStrategies
public const string EfficiencyFirst = "效率优先"; public const string EfficiencyFirst = "效率优先";
public const string Balanced = "平衡模式"; public const string Balanced = "平衡模式";
// 权重顺序: 安全、效率、转弯、曲折、冗余 // 权重顺序: 安全、效率、转弯、直达
public static readonly Dictionary<string, double[]> Weights = new Dictionary<string, double[]> public static readonly Dictionary<string, double[]> Weights = new Dictionary<string, double[]>
{ {
[SafetyFirst] = new[] { 0.50, 0.20, 0.10, 0.10, 0.10 }, [SafetyFirst] = new[] { 0.50, 0.30, 0.10, 0.10 },
[EfficiencyFirst] = new[] { 0.20, 0.40, 0.15, 0.15, 0.10 }, [EfficiencyFirst] = new[] { 0.20, 0.50, 0.15, 0.15 },
[Balanced] = new[] { 0.35, 0.30, 0.10, 0.15, 0.10 } [Balanced] = new[] { 0.35, 0.35, 0.15, 0.15 }
}; };
} }
``` ```
--- ---
## 三、指标体系(5维度) ## 三、指标体系(4维度)
### 3.1 安全性(碰撞风险指数)- 权重最高 ### 3.1 安全性(碰撞风险指数)- 权重最高
@ -108,7 +110,7 @@ RadiusPenalty = MinRadius < 2.0 ? 20 : 0;
TurnDifficultyScore = Max(0, 100 - TurnPenalty - RadiusPenalty); TurnDifficultyScore = Max(0, 100 - TurnPenalty - RadiusPenalty);
``` ```
### 3.4 平顺性(路径曲折度) ### 3.4 直达性(路径曲折度)
```csharp ```csharp
// 计算起点到终点的直线距离 // 计算起点到终点的直线距离
@ -123,27 +125,7 @@ Tortuosity = TotalLength / StraightDistance;
TortuosityScore = Max(0, 100 - (Tortuosity - 1.0) * 50); TortuosityScore = Max(0, 100 - (Tortuosity - 1.0) * 50);
``` ```
### 3.5 冗余度(通道冗余度)
```csharp
// 获取路径经过的通道
Channels = GetChannelsAlongPath(this);
// 通道最小限宽限高
MinChannelWidth = Channels.Min(c => c.WidthLimit);
MinChannelHeight = Channels.Min(c => c.HeightLimit);
// 车辆实际尺寸(含安全间隙)
EffectiveVehicleWidth = MaxVehicleWidth + SafetyMargin;
EffectiveVehicleHeight = MaxVehicleHeight + SafetyMargin;
// 冗余率
WidthRedundancy = (MinChannelWidth - EffectiveVehicleWidth) / EffectiveVehicleWidth;
HeightRedundancy = (MinChannelHeight - EffectiveVehicleHeight) / EffectiveVehicleHeight;
// 冗余度分0-100
RedundancyScore = Min(100, (WidthRedundancy + HeightRedundancy) / 2 * 100);
```
--- ---
@ -234,6 +216,7 @@ public PathRoute FindBestPathInGroup(EndpointGroup group, string strategy)
## 五、碰撞热点计算 ## 五、碰撞热点计算
### 5.1 热点定义 ### 5.1 热点定义
- **范围**: 3米半径球体 - **范围**: 3米半径球体
- **阈值**: 范围内 ≥2 次碰撞 - **阈值**: 范围内 ≥2 次碰撞
- **数据来源**: `ClashDetectiveCollisionObjects` 表的 `Item1PosX/Y/Z` - **数据来源**: `ClashDetectiveCollisionObjects` 表的 `Item1PosX/Y/Z`
@ -295,7 +278,7 @@ public List<CollisionHotspot> DetectHotspots(List<CollisionResult> collisions, d
| **安全建议** | 碰撞数>0 | 发现X次碰撞其中Y个热点建议检查碰撞位置 | | **安全建议** | 碰撞数>0 | 发现X次碰撞其中Y个热点建议检查碰撞位置 |
| **效率建议** | 长度差异率>30% | 该路径比组内最短路径长X%,建议优化路线 | | **效率建议** | 长度差异率>30% | 该路径比组内最短路径长X%,建议优化路线 |
| **转弯建议** | 转弯次数>3 | 路径包含X个转弯建议减少急转弯 | | **转弯建议** | 转弯次数>3 | 路径包含X个转弯建议减少急转弯 |
| **冗余建议** | 冗余度<20% | 通道余量较小建议确认车辆尺寸匹配 | | **直达建议** | 直达性<70% | 路径较为曲折建议考虑更直接的路线 |
| **组内对比** | 组内路径>1 | 本组共X条路径推荐【路径名】为最佳选择 | | **组内对比** | 组内路径>1 | 本组共X条路径推荐【路径名】为最佳选择 |
### 6.2 建议生成示例 ### 6.2 建议生成示例
@ -344,8 +327,7 @@ public List<string> GenerateSuggestions(PathDetailedAnalysis analysis, EndpointG
-- AnalysisResults 表扩展字段 -- AnalysisResults 表扩展字段
ALTER TABLE AnalysisResults ADD COLUMN ALTER TABLE AnalysisResults ADD COLUMN
TurnDifficultyScore REAL, -- 转弯难度分 TurnDifficultyScore REAL, -- 转弯难度分
TortuosityScore REAL, -- 曲折度分 TortuosityScore REAL, -- 直达性分
RedundancyScore REAL, -- 冗余度分
HotspotCount INTEGER, -- 热点数量 HotspotCount INTEGER, -- 热点数量
AnalysisStrategy TEXT, -- 分析策略 AnalysisStrategy TEXT, -- 分析策略
GroupId TEXT, -- 所属终点组ID GroupId TEXT, -- 所属终点组ID
@ -399,16 +381,15 @@ CREATE TABLE IF NOT EXISTS CollisionHotspots (
| 任务 | 文件 | 说明 | | 任务 | 文件 | 说明 |
|-----|------|------| |-----|------|------|
| 2.1 ViewModel更新 | `src/UI/WPF/ViewModels/PathAnalysisViewModel.cs` | 修改,增加新属性和命令 | | 2.1 ViewModel更新 | `src/UI/WPF/ViewModels/PathAnalysisViewModel.cs` | 修改,增加新属性和命令 |
| 2.2 对话框更新 | `src/UI/WPF/Views/PathAnalysisDialog.xaml` | 修改,增加可视化元素 | | 2.2 对话框更新 | `src/UI/WPF/Views/PathAnalysisDialog.xaml` | 修改,增加Canvas图表区域 |
| 2.3 图表组件 | `src/UI/WPF/Views/PathAnalysisCharts.xaml` | 新建,雷达图/柱状图 | | 2.3 图表绘制 | `src/UI/WPF/Views/PathAnalysisDialog.xaml.cs` | 修改Canvas自绘柱状图和雷达图 |
### Phase 3: HTML报告第三步 ### Phase 3: HTML报告第三步
| 任务 | 文件 | 说明 | | 任务 | 文件 | 说明 |
|-----|------|------| |-----|------|------|
| 3.1 报告生成器 | `src/Core/PathAnalysisReportGenerator.cs` | 新建HTML生成逻辑 | | 3.1 报告生成器 | `src/Core/PathAnalysisReportGenerator.cs` | 新建HTML生成逻辑 |
| 3.2 Chart.js部署 | `resources/js/chart.min.js` | 下载本地Chart.js | | 3.2 报告样式 | 嵌入式CSS | 使用内联样式,无需外部依赖 |
| 3.3 报告模板 | `resources/templates/` | 新建HTML模板文件 |
--- ---
@ -425,16 +406,14 @@ public double CalculateWeightedScore(PathDetailedAnalysis analysis, string strat
double safety = analysis.SafetyScore; // 安全 double safety = analysis.SafetyScore; // 安全
double efficiency = analysis.EfficiencyScore; // 效率 double efficiency = analysis.EfficiencyScore; // 效率
double turn = analysis.TurnDifficultyScore; // 转弯 double turn = analysis.TurnDifficultyScore; // 转弯
double tortuosity = analysis.TortuosityScore; // 曲折 double tortuosity = analysis.TortuosityScore; // 直达
double redundancy = analysis.RedundancyScore; // 冗余
// 加权计算 // 加权计算
double weightedScore = double weightedScore =
safety * weights[0] + safety * weights[0] +
efficiency * weights[1] + efficiency * weights[1] +
turn * weights[2] + turn * weights[2] +
tortuosity * weights[3] + tortuosity * weights[3];
redundancy * weights[4];
return Math.Round(weightedScore, 1); return Math.Round(weightedScore, 1);
} }
@ -464,7 +443,6 @@ public PathDetailedAnalysis AnalyzePath(PathRoute route, AnalysisContext context
analysis.EfficiencyScore = CalculateEfficiencyScore(route, context.GroupMinTime); analysis.EfficiencyScore = CalculateEfficiencyScore(route, context.GroupMinTime);
analysis.TurnDifficultyScore = CalculateTurnDifficultyScore(route.Edges); analysis.TurnDifficultyScore = CalculateTurnDifficultyScore(route.Edges);
analysis.TortuosityScore = CalculateTortuosityScore(route); analysis.TortuosityScore = CalculateTortuosityScore(route);
analysis.RedundancyScore = CalculateRedundancyScore(route, context.Channels);
// 4. 计算综合评分 // 4. 计算综合评分
analysis.WeightedScore = CalculateWeightedScore(analysis, context.Strategy); analysis.WeightedScore = CalculateWeightedScore(analysis, context.Strategy);
@ -484,35 +462,46 @@ public PathDetailedAnalysis AnalyzePath(PathRoute route, AnalysisContext context
| 路径分析 - 多路径对比 [关闭] | | 路径分析 - 多路径对比 [关闭] |
+----------------------------------------------------------+ +----------------------------------------------------------+
| +----------------+ +------------------+ +-----------+ | | +----------------+ +------------------+ +-----------+ |
| | 路径选择 | | 对比分析表格 | | 分析结果 | | | | 路径选择 | | 🏆 推荐最佳路径 | | 💡 优化建议| |
| | - 路径A | | 名称|安全|效率|... | | 最佳路径 | | | | - 路径A | | 路径名称 | | 建议1... | |
| | - 路径B [x] | | A | 90 | 85 |... | | 优化建议 | | | | - 路径B [x] | | 长度: XXm | 评分 | | 建议2... | |
| | - 路径C [x] | | B |100 | 70 |... | | ... | | | | - 路径C [x] | +------------------+ | ... | |
| | | | C | 80 | 90 |... | | | | | | | +------------------+ +-----------+ |
| | [全选][清空] | +------------------+ +-----------+ | | | [全选][清空] | | 📊 多路径对比图表 | |
| | | +------------------+ | | | | | +--------------+ | |
| | 策略: | | 雷达图可视化 | | | | 策略: | | | 柱状图 | | |
| | ○ 安全优先 | | | | | | ○ 安全优先 | | | (4维度对比) | | |
| | ○ 效率优先 | +------------------+ | | | ○ 效率优先 | | +--------------+ | |
| | ● 平衡模式 | +------------------+ | | | ● 平衡模式 | | +--------------+ | |
| | | | 同终点组分析 | | | | | | | 雷达图 | | |
| | [开始分析] | | 组1: 终点A (3条) | | | | [开始分析] | | | (组内对比) | | |
| +----------------+ | - 路径B [推荐] | | | +----------------+ +------------------+ |
| +------------------+ |
| | 🎯 同终点路径组 | |
| | 组1: 终点 (2条) | |
| | - 路径B (最佳) | |
| | - 路径A | | | | - 路径A | |
| | - 路径C | |
| +------------------+ | | +------------------+ |
+----------------------------------------------------------+ +----------------------------------------------------------+
| [导出报告] [关闭] | | [导出报告] [关闭] |
+----------------------------------------------------------+ +----------------------------------------------------------+
``` ```
### 11.2 可视化元素 ### 11.2 可视化元素WPF Canvas自绘
1. **雷达图**: 展示单条路径5维度分数 1. **柱状图**: 使用WPF Canvas绘制展示各路径4维度分数对比安全/效率/转弯/直达)
2. **柱状图**: 组内路径长度/时间对比 - 每个柱子显示具体分数
3. **表格**: 所有路径的详细指标 - 路径名称水平显示在柱组下方
4. **推荐卡片**: 高亮显示最佳路径 - 右上角显示图例
5. **热点标记**: 在路径上标注碰撞热点位置
2. **雷达图**: 使用WPF Canvas绘制展示选中组内所有路径的4维度表现
- 五边形网格背景
- 不同颜色区分各路径
- 最佳路径用粗线标识
3. **推荐卡片**: 显示最佳路径名称、长度和综合评分
4. **优化建议列表**: 分类显示安全、效率、转弯等建议
--- ---
@ -524,15 +513,15 @@ public PathDetailedAnalysis AnalyzePath(PathRoute route, AnalysisContext context
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>路径分析报告</title> <title>路径分析报告</title>
<!-- 本地Chart.js -->
<script src="./js/chart.min.js"></script>
<style> <style>
/* 简洁专业风格(蓝白配色) */ /* 简洁专业风格(蓝白配色) */
:root { --primary: #2c5aa0; --success: #28a745; --warning: #ffc107; --danger: #dc3545; } :root { --primary: #2c5aa0; --success: #28a745; --warning: #ffc107; --danger: #dc3545; }
body { font-family: 'Microsoft YaHei', Arial; margin: 40px; } body { font-family: 'Microsoft YaHei', Arial; margin: 40px; }
.header { text-align: center; border-bottom: 3px solid var(--primary); padding-bottom: 20px; } .header { text-align: center; border-bottom: 3px solid var(--primary); padding-bottom: 20px; }
.best-path-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 12px; } .best-path-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 12px; }
.metric-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 15px; } .score-bar-container { background: #e9ecef; height: 24px; border-radius: 12px; position: relative; }
.score-bar { height: 100%; border-radius: 12px; }
.score-text { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); font-weight: bold; }
.comparison-table { width: 100%; border-collapse: collapse; } .comparison-table { width: 100%; border-collapse: collapse; }
.comparison-table th { background: var(--primary); color: white; padding: 12px; } .comparison-table th { background: var(--primary); color: white; padding: 12px; }
/* ... */ /* ... */
@ -541,15 +530,15 @@ public PathDetailedAnalysis AnalyzePath(PathRoute route, AnalysisContext context
<body> <body>
<!-- 1. 头部 --> <!-- 1. 头部 -->
<!-- 2. 执行摘要+最佳路径 --> <!-- 2. 执行摘要+最佳路径 -->
<!-- 3. 雷达图 --> <!-- 3. 同终点组分析 -->
<!-- 4. 同终点组分析 --> <!-- 4. 详细数据表格(带分数条) -->
<!-- 5. 详细数据表格 --> <!-- 5. 优化建议 -->
<!-- 6. 优化建议 -->
<!-- 7. 技术参数 -->
</body> </body>
</html> </html>
``` ```
**说明**: HTML报告使用纯CSS样式无需外部JavaScript库可离线打开。
--- ---
## 十三、风险评估与应对 ## 十三、风险评估与应对
@ -566,25 +555,43 @@ public PathDetailedAnalysis AnalyzePath(PathRoute route, AnalysisContext context
## 十四、验收标准 ## 十四、验收标准
### 14.1 Phase 1 验收 ### 14.1 Phase 1 验收
- [ ] 能正确计算5维度分数 - [ ] 能正确计算5维度分数
- [ ] 能正确检测碰撞热点3米范围 - [ ] 能正确检测碰撞热点3米范围
- [ ] 能按策略计算加权评分 - [ ] 能按策略计算加权评分
- [ ] 数据能正确保存到数据库 - [ ] 数据能正确保存到数据库
### 14.2 Phase 2 验收 ### 14.2 Phase 2 验收
- [ ] UI能显示5维度雷达图
- [ ] UI能显示4维度柱状图WPF Canvas绘制
- [ ] UI能显示雷达图WPF Canvas绘制
- [ ] 同终点组能正确分组2米阈值 - [ ] 同终点组能正确分组2米阈值
- [ ] 能正确标识组内最佳路径 - [ ] 能正确标识组内最佳路径
- [ ] 优化建议具体且可执行 - [ ] 优化建议具体且可执行
### 14.3 Phase 3 验收 ### 14.3 Phase 3 验收
- [ ] HTML报告能离线打开使用本地Chart.js
- [ ] HTML报告能离线打开纯CSS样式无外部依赖
- [ ] 报告包含所有必要信息 - [ ] 报告包含所有必要信息
- [ ] 表能正确显示 - [ ] 表格分数条能正确显示
- [ ] 样式符合专业报告标准 - [ ] 样式符合专业报告标准
--- ---
## 十五、资源文件路径
所有资源文件统一存放在 `resources` 目录下:
| 文件 | 路径 | 说明 |
|-----|------|------|
| 默认配置 | `resources/default_config.toml` | 系统默认配置模板 |
| 插件名称 | `resources/TransportPlugin.name.txt` | 插件显示名称 |
| 单位立方体 | `resources/unit_cube.nwc` | 虚拟车辆碰撞检测用 |
**部署说明**: 编译时资源文件会自动复制到输出目录,部署脚本会将整个 `resources` 文件夹复制到插件目录。
---
## 附录:参考文件 ## 附录:参考文件
- 现有碰撞报告生成器: `src/Utils/CollisionReportHtmlGenerator.cs` - 现有碰撞报告生成器: `src/Utils/CollisionReportHtmlGenerator.cs`

View File

@ -0,0 +1,29 @@
# Navisworks Transport Plugin Localization File
# This file contains localized strings for the plugin
# Plugin Display Names
HelloWorldText=物流路径规划插件已加载
PluginDisplayName=物流路径规划
PluginToolTip=物流路径规划和动画仿真插件
# Tab Names
ModelSettingsTab=类别设置
PathEditingTab=路径编辑
AnimationControlTab=检测动画
SystemManagementTab=系统管理
# Button Labels
ShowAllButton=显示全部
HideAllButton=隐藏全部
RefreshButton=刷新
NewPathButton=新建路径
StartEditButton=开始编辑
StartAnimationButton=开始动画
HelpButton=帮助
AboutButton=关于
# Status Messages
InitializingPlugin=正在初始化插件...
PluginReady=插件就绪
SelectModelsInstruction=请在主界面中点击选择模型
OpenModelInstruction=请先打开一个Navisworks模型文件

View File

@ -1,48 +0,0 @@
# Unit Cube 1m x 1m x 1m
# Bottom center at origin (0, 0, 0)
# For Navisworks Virtual Vehicle
# Vertices (8 corners)
# Bottom face (Z = 0)
v -0.5 -0.5 0.0
v 0.5 -0.5 0.0
v 0.5 0.5 0.0
v -0.5 0.5 0.0
# Top face (Z = 1)
v -0.5 -0.5 1.0
v 0.5 -0.5 1.0
v 0.5 0.5 1.0
v -0.5 0.5 1.0
# Normals
vn 0 0 -1
vn 0 0 1
vn 0 -1 0
vn 0 1 0
vn -1 0 0
vn 1 0 0
# Faces (6 faces, 2 triangles each)
# Bottom face (Z = 0, normal -Z)
f 1//1 3//1 2//1
f 1//1 4//1 3//1
# Top face (Z = 1, normal +Z)
f 5//2 6//2 7//2
f 5//2 7//2 8//2
# Front face (Y = -0.5, normal -Y)
f 1//3 2//3 6//3
f 1//3 6//3 5//3
# Back face (Y = 0.5, normal +Y)
f 3//4 4//4 8//4
f 3//4 8//4 7//4
# Left face (X = -0.5, normal -X)
f 1//5 5//5 8//5
f 1//5 8//5 4//5
# Right face (X = 0.5, normal +X)
f 2//6 3//6 7//6
f 2//6 7//6 6//6

View File

@ -130,7 +130,7 @@ namespace NavisworksTransport.Core.Config
{ {
// 从插件当前目录获取模板文件 // 从插件当前目录获取模板文件
var pluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); var pluginDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
return Path.Combine(pluginDirectory, "default_config.toml"); return Path.Combine(pluginDirectory, "resources", "default_config.toml");
} }
} }

View File

@ -198,11 +198,13 @@ namespace NavisworksTransport.Core
.score-bar.low { background: #DC3545; } .score-bar.low { background: #DC3545; }
.score-text { .score-text {
position: absolute; position: absolute;
right: 10px; left: 50%;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translate(-50%, -50%);
font-weight: bold; font-weight: bold;
font-size: 12px; font-size: 12px;
color: #333;
text-shadow: 0 0 2px rgba(255,255,255,0.8);
} }
/* 建议样式 */ /* 建议样式 */
@ -363,7 +365,6 @@ namespace NavisworksTransport.Core
sb.AppendLine(" <tr>"); sb.AppendLine(" <tr>");
sb.AppendLine(" <th>路径名称</th>"); sb.AppendLine(" <th>路径名称</th>");
sb.AppendLine(" <th>长度(m)</th>"); sb.AppendLine(" <th>长度(m)</th>");
sb.AppendLine(" <th>时间(s)</th>");
sb.AppendLine(" <th>碰撞</th>"); sb.AppendLine(" <th>碰撞</th>");
sb.AppendLine(" <th>安全分</th>"); sb.AppendLine(" <th>安全分</th>");
sb.AppendLine(" <th>效率分</th>"); sb.AppendLine(" <th>效率分</th>");

View File

@ -379,13 +379,11 @@ namespace NavisworksTransport.Core
var assemblyLocation = Assembly.GetExecutingAssembly().Location; var assemblyLocation = Assembly.GetExecutingAssembly().Location;
var pluginDir = Path.GetDirectoryName(assemblyLocation); var pluginDir = Path.GetDirectoryName(assemblyLocation);
// 尝试多个可能的位置 // 尝试多个可能的位置现在统一放在resources目录下
var possiblePaths = new[] var possiblePaths = new[]
{ {
Path.Combine(pluginDir, "resources", "unit_cube.nwc"), Path.Combine(pluginDir, "resources", "unit_cube.nwc"),
Path.Combine(pluginDir, "unit_cube.nwc"), // 开发时的备用位置
Path.Combine(pluginDir, "..", "resources", "unit_cube.nwc"),
// 开发时的位置
@"c:\Users\Tellme\apps\NavisworksTransport\resources\unit_cube.nwc" @"c:\Users\Tellme\apps\NavisworksTransport\resources\unit_cube.nwc"
}; };

View File

@ -11,6 +11,15 @@ using NavisworksTransport.Core.Models;
namespace NavisworksTransport.UI.WPF.ViewModels namespace NavisworksTransport.UI.WPF.ViewModels
{ {
/// <summary>
/// 分析完成事件参数
/// </summary>
public class AnalysisCompletedEventArgs : EventArgs
{
public List<EndpointGroupViewModel> EndpointGroups { get; set; }
public EndpointGroupViewModel SelectedGroup { get; set; }
}
/// <summary> /// <summary>
/// 路径分析视图模型 - 扩展版 /// 路径分析视图模型 - 扩展版
/// 支持多维度评分和同终点组分析 /// 支持多维度评分和同终点组分析
@ -140,6 +149,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// </summary> /// </summary>
public ObservableCollection<DimensionScoreViewModel> DimensionScores { get; set; } public ObservableCollection<DimensionScoreViewModel> DimensionScores { get; set; }
/// <summary>
/// 是否有分析结果(用于控制图表显示)
/// </summary>
public bool HasAnalysisResults => EndpointGroups != null && EndpointGroups.Count > 0;
/// <summary>
/// 分析完成事件用于通知UI绘制图表
/// </summary>
public event EventHandler<AnalysisCompletedEventArgs> AnalysisCompleted;
/// <summary> /// <summary>
/// 命令 /// 命令
/// </summary> /// </summary>
@ -385,6 +404,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
LogManager.Info($"[路径分析UI] 选中第一个组: {SelectedGroup.GroupName}"); LogManager.Info($"[路径分析UI] 选中第一个组: {SelectedGroup.GroupName}");
} }
OnPropertyChanged(nameof(HasAnalysisResults));
// 触发分析完成事件通知UI绘制图表
AnalysisCompleted?.Invoke(this, new AnalysisCompletedEventArgs
{
EndpointGroups = EndpointGroups.ToList(),
SelectedGroup = SelectedGroup
});
LogManager.Info($"[路径分析UI] 更新完成: EndpointGroups={EndpointGroups.Count}, Suggestions={CategorizedSuggestions.Count}"); LogManager.Info($"[路径分析UI] 更新完成: EndpointGroups={EndpointGroups.Count}, Suggestions={CategorizedSuggestions.Count}");
} }
@ -411,6 +439,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{ {
SelectedPathAnalysis = SelectedGroup.BestPath; SelectedPathAnalysis = SelectedGroup.BestPath;
UpdateDimensionScores(SelectedGroup.BestPath); UpdateDimensionScores(SelectedGroup.BestPath);
// 选中组变更时触发事件(更新雷达图)
AnalysisCompleted?.Invoke(this, new AnalysisCompletedEventArgs
{
EndpointGroups = EndpointGroups.ToList(),
SelectedGroup = SelectedGroup
});
} }
} }

View File

@ -194,27 +194,19 @@ NavisworksTransport 路径规划分析对话框 V2
<!-- 最佳路径卡片 --> <!-- 最佳路径卡片 -->
<Border Style="{StaticResource BestPathCardStyle}" <Border Style="{StaticResource BestPathCardStyle}"
Visibility="{Binding RecommendedPath, Converter={StaticResource NullToVisibilityConverter}}"> Visibility="{Binding RecommendedPath, Converter={StaticResource NullToVisibilityConverter}}">
<Grid> <StackPanel>
<Grid.ColumnDefinitions> <TextBlock FontSize="12" Foreground="#FF28A745" FontWeight="SemiBold">
<ColumnDefinition Width="*"/> 🏆 推荐最佳路径
<ColumnDefinition Width="Auto"/> </TextBlock>
</Grid.ColumnDefinitions> <TextBlock Text="{Binding RecommendedPath.Name}" FontSize="18" FontWeight="Bold"
<StackPanel Grid.Column="0"> Foreground="#FF2C3E50" Margin="0,5"/>
<TextBlock FontSize="12" Foreground="#FF28A745" FontWeight="SemiBold"> <TextBlock FontSize="12" Foreground="#FF6C757D">
🏆 推荐最佳路径 <Run Text="长度: "/>
</TextBlock> <Run Text="{Binding RecommendedPath.Length, StringFormat={}{0:F1}m}"/>
<TextBlock Text="{Binding RecommendedPath.Name}" FontSize="18" FontWeight="Bold" <Run Text=" | 评分: "/>
Foreground="#FF2C3E50" Margin="0,5"/> <Run Text="{Binding RecommendedPath.Score, StringFormat={}{0:F1}}"/>
<TextBlock FontSize="12" Foreground="#FF6C757D"> </TextBlock>
<Run Text="长度: "/> </StackPanel>
<Run Text="{Binding RecommendedPath.Length, StringFormat={}{0:F1}m}"/>
<Run Text=" | 评分: "/>
<Run Text="{Binding RecommendedPath.Score, StringFormat={}{0:F1}}"/>
</TextBlock>
</StackPanel>
<Button Grid.Column="1" Content="高亮显示" Command="{Binding HighlightBestPathCommand}"
Style="{StaticResource ActionButtonStyle}" Width="90" Height="32"/>
</Grid>
</Border> </Border>
<!-- 同终点组列表 --> <!-- 同终点组列表 -->
@ -252,31 +244,35 @@ NavisworksTransport 路径规划分析对话框 V2
</StackPanel> </StackPanel>
</Border> </Border>
<!-- 五维度分数展示 --> <!-- 图表展示区域 -->
<Border Style="{StaticResource CardStyle}" Margin="0,10,0,0"> <Border Style="{StaticResource CardStyle}" Margin="0,10,0,0"
Visibility="{Binding HasAnalysisResults, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel> <StackPanel>
<TextBlock Text="📊 四维度评分" FontSize="14" FontWeight="Bold" <TextBlock Text="📊 多路径对比图表" FontSize="14" FontWeight="Bold"
Foreground="{StaticResource PrimaryBrush}" Margin="0,0,0,10"/> Foreground="{StaticResource PrimaryBrush}" Margin="0,0,0,10"/>
<ItemsControl ItemsSource="{Binding DimensionScores}"> <!-- 柱状图 -->
<ItemsControl.ItemTemplate> <Border BorderBrush="#FFE0E0E0" BorderThickness="1" CornerRadius="4" Height="280" Margin="0,0,0,10">
<DataTemplate> <Grid>
<Grid Margin="0,6"> <TextBlock Text="各维度分数对比" FontSize="12" FontWeight="Bold"
<Grid.ColumnDefinitions> Foreground="#2C5AA0" HorizontalAlignment="Center" Margin="0,5"/>
<ColumnDefinition Width="80"/> <Canvas x:Name="BarChartCanvas" Margin="10,30,10,10"
<ColumnDefinition Width="*"/> Width="530" Height="240"/>
<ColumnDefinition Width="50"/> </Grid>
</Grid.ColumnDefinitions> </Border>
<TextBlock Grid.Column="0" Text="{Binding Name}" FontSize="11"
VerticalAlignment="Center"/> <!-- 雷达图 -->
<ProgressBar Grid.Column="1" Value="{Binding Score}" <Border BorderBrush="#FFE0E0E0" BorderThickness="1" CornerRadius="4" Height="200">
Maximum="{Binding MaxScore}" Style="{StaticResource ScoreBarStyle}"/> <Grid>
<TextBlock Grid.Column="2" Text="{Binding Score, StringFormat={}{0:F1}}" <TextBlock Text="雷达图对比" FontSize="12" FontWeight="Bold"
FontSize="11" FontWeight="Bold" HorizontalAlignment="Right"/> Foreground="#2C5AA0" HorizontalAlignment="Center" Margin="0,5"/>
</Grid> <Canvas x:Name="RadarChartCanvas" Margin="10,30,10,10"
</DataTemplate> Width="530" Height="160"/>
</ItemsControl.ItemTemplate> </Grid>
</ItemsControl> </Border>
<TextBlock Text="提示:柱形图对比各路径维度得分,雷达图展示选中组内所有路径的四维度表现"
FontSize="10" Foreground="#FF6C757D" Margin="0,8,0,0" TextWrapping="Wrap"/>
</StackPanel> </StackPanel>
</Border> </Border>
</StackPanel> </StackPanel>
@ -318,9 +314,6 @@ NavisworksTransport 路径规划分析对话框 V2
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
</ScrollViewer> </ScrollViewer>
<Button Grid.Row="2" Content="导出分析报告" Command="{Binding ExportReportCommand}"
Style="{StaticResource ActionButtonStyle}" Height="38" Margin="0,10,0,0"/>
</Grid> </Grid>
</Border> </Border>
</Grid> </Grid>
@ -329,8 +322,8 @@ NavisworksTransport 路径规划分析对话框 V2
<Border Grid.Row="2" Background="White" BorderBrush="#FFE0E0E0" BorderThickness="0,1,0,0" <Border Grid.Row="2" Background="White" BorderBrush="#FFE0E0E0" BorderThickness="0,1,0,0"
Padding="20,12"> Padding="20,12">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="重新分析" Style="{StaticResource SecondaryButtonStyle}" Width="100" Margin="0,0,10,0"/> <Button Content="导出报告" Command="{Binding ExportReportCommand}"
<Button Content="应用推荐" Style="{StaticResource ActionButtonStyle}" Width="100" Margin="0,0,10,0"/> Style="{StaticResource ActionButtonStyle}" Width="100" Margin="0,0,10,0"/>
<Button Content="关闭" Click="CloseButton_Click" Style="{StaticResource SecondaryButtonStyle}" Width="80"/> <Button Content="关闭" Click="CloseButton_Click" Style="{StaticResource SecondaryButtonStyle}" Width="80"/>
</StackPanel> </StackPanel>
</Border> </Border>

View File

@ -1,9 +1,14 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Shapes;
using NavisworksTransport.Core;
using NavisworksTransport.Core.Models;
using NavisworksTransport.UI.WPF.ViewModels; using NavisworksTransport.UI.WPF.ViewModels;
namespace NavisworksTransport.UI.WPF.Views namespace NavisworksTransport.UI.WPF.Views
@ -26,7 +31,12 @@ namespace NavisworksTransport.UI.WPF.Views
try try
{ {
InitializeComponent(); InitializeComponent();
DataContext = new ViewModels.PathAnalysisViewModel(); var viewModel = new ViewModels.PathAnalysisViewModel();
DataContext = viewModel;
// 订阅分析完成事件,绘制图表
viewModel.AnalysisCompleted += OnAnalysisCompleted;
LogManager.Info("PathAnalysisDialog初始化完成"); LogManager.Info("PathAnalysisDialog初始化完成");
Title = $"路径规划分析 - 多路径对比 [{DateTime.Now:MM-dd HH:mm}]"; Title = $"路径规划分析 - 多路径对比 [{DateTime.Now:MM-dd HH:mm}]";
} }
@ -36,6 +46,339 @@ namespace NavisworksTransport.UI.WPF.Views
} }
} }
/// <summary>
/// 分析完成时绘制图表
/// </summary>
private void OnAnalysisCompleted(object sender, AnalysisCompletedEventArgs e)
{
try
{
Dispatcher.Invoke(() =>
{
DrawBarChart(e.EndpointGroups);
DrawRadarChart(e.SelectedGroup);
});
}
catch (Exception ex)
{
LogManager.Error($"绘制图表失败: {ex.Message}");
}
}
/// <summary>
/// 绘制柱状图
/// </summary>
private void DrawBarChart(List<EndpointGroupViewModel> groups)
{
if (groups == null || groups.Count == 0) return;
var canvas = BarChartCanvas;
canvas.Children.Clear();
// 收集所有路径数据
var allPaths = new List<PathDetailedAnalysis>();
foreach (var group in groups)
{
allPaths.AddRange(group.PathAnalyses);
}
if (allPaths.Count == 0) return;
// 限制显示数量
var displayPaths = allPaths.Take(8).ToList();
double canvasWidth = canvas.Width;
double canvasHeight = canvas.Height;
double marginLeft = 60;
double marginRight = 20;
double marginTop = 30;
double marginBottom = 50;
double chartWidth = canvasWidth - marginLeft - marginRight;
double chartHeight = canvasHeight - marginTop - marginBottom;
// 绘制坐标轴
DrawAxis(canvas, marginLeft, marginTop, chartWidth, chartHeight);
// 四个维度的颜色
var colors = new[] {
Color.FromRgb(40, 167, 69), // 安全性 - 绿
Color.FromRgb(0, 123, 255), // 效率 - 蓝
Color.FromRgb(255, 193, 7), // 转弯 - 黄
Color.FromRgb(23, 162, 184) // 直达性 - 青
};
var dimensionNames = new[] { "安全", "效率", "转弯", "直达" };
int barGroupCount = displayPaths.Count;
double groupWidth = chartWidth / barGroupCount;
double barWidth = groupWidth * 0.7 / 4; // 4个柱子
for (int i = 0; i < displayPaths.Count; i++)
{
var analysis = displayPaths[i];
double groupX = marginLeft + i * groupWidth + groupWidth * 0.15;
double[] scores = new[] { analysis.SafetyScore, analysis.EfficiencyScore,
analysis.TurnDifficultyScore, analysis.TortuosityScore };
// 绘制每个维度的柱子
for (int j = 0; j < 4; j++)
{
double barHeight = scores[j] / 100.0 * chartHeight;
double barX = groupX + j * barWidth;
double barY = marginTop + chartHeight - barHeight;
var rect = new Rectangle
{
Width = barWidth - 2,
Height = barHeight,
Fill = new SolidColorBrush(colors[j]),
RadiusX = 2,
RadiusY = 2
};
Canvas.SetLeft(rect, barX);
Canvas.SetTop(rect, barY);
canvas.Children.Add(rect);
// 在柱子上方显示分数
var scoreText = new TextBlock
{
Text = scores[j].ToString("F0"),
FontSize = 8,
FontWeight = FontWeights.Bold,
Foreground = new SolidColorBrush(colors[j]),
TextAlignment = TextAlignment.Center,
Width = barWidth
};
Canvas.SetLeft(scoreText, barX);
Canvas.SetTop(scoreText, barY - 12);
canvas.Children.Add(scoreText);
}
// 路径名称(水平显示)
var label = new TextBlock
{
Text = analysis.RouteName,
FontSize = 9,
Foreground = new SolidColorBrush(Colors.Gray),
TextAlignment = TextAlignment.Center,
Width = groupWidth * 0.85,
TextWrapping = TextWrapping.Wrap
};
Canvas.SetLeft(label, groupX);
Canvas.SetTop(label, marginTop + chartHeight + 5);
canvas.Children.Add(label);
}
// 绘制图例
DrawLegend(canvas, dimensionNames, colors, canvasWidth - 150, 5);
}
/// <summary>
/// 绘制雷达图
/// </summary>
private void DrawRadarChart(EndpointGroupViewModel selectedGroup)
{
if (selectedGroup?.PathAnalyses == null || selectedGroup.PathAnalyses.Count == 0) return;
var canvas = RadarChartCanvas;
canvas.Children.Clear();
var analyses = selectedGroup.PathAnalyses.Take(5).ToList();
double canvasWidth = canvas.Width;
double canvasHeight = canvas.Height;
double centerX = canvasWidth / 2;
double centerY = canvasHeight / 2 + 10;
double radius = Math.Min(canvasWidth, canvasHeight) / 2 - 30;
var dimensions = new[] { "安全性", "效率", "转弯难度", "直达性" };
int dimensionCount = dimensions.Length;
// 绘制网格
for (int i = 1; i <= 5; i++)
{
double r = radius * i / 5;
var polygon = new Polygon
{
Stroke = new SolidColorBrush(Color.FromRgb(200, 200, 200)),
StrokeThickness = 0.5,
Fill = Brushes.Transparent
};
for (int j = 0; j < dimensionCount; j++)
{
double angle = j * 2 * Math.PI / dimensionCount - Math.PI / 2;
double x = centerX + r * Math.Cos(angle);
double y = centerY + r * Math.Sin(angle);
polygon.Points.Add(new Point(x, y));
}
canvas.Children.Add(polygon);
}
// 绘制轴线和标签
for (int i = 0; i < dimensionCount; i++)
{
double angle = i * 2 * Math.PI / dimensionCount - Math.PI / 2;
double x = centerX + radius * Math.Cos(angle);
double y = centerY + radius * Math.Sin(angle);
// 轴线
var line = new Line
{
X1 = centerX,
Y1 = centerY,
X2 = x,
Y2 = y,
Stroke = new SolidColorBrush(Color.FromRgb(200, 200, 200)),
StrokeThickness = 0.5
};
canvas.Children.Add(line);
// 标签
var labelX = centerX + (radius + 20) * Math.Cos(angle);
var labelY = centerY + (radius + 20) * Math.Sin(angle);
var label = new TextBlock
{
Text = dimensions[i],
FontSize = 10,
FontWeight = FontWeights.Bold,
Foreground = new SolidColorBrush(Color.FromRgb(44, 90, 160))
};
Canvas.SetLeft(label, labelX - 15);
Canvas.SetTop(label, labelY - 8);
canvas.Children.Add(label);
}
// 绘制数据
var pathColors = new[] {
Colors.Red, Colors.Blue, Colors.Green, Colors.Orange, Colors.Purple
};
for (int i = 0; i < analyses.Count; i++)
{
var analysis = analyses[i];
double[] scores = new[] { analysis.SafetyScore, analysis.EfficiencyScore,
analysis.TurnDifficultyScore, analysis.TortuosityScore };
var polygon = new Polygon
{
Stroke = new SolidColorBrush(pathColors[i % pathColors.Length]),
StrokeThickness = analysis.IsBestInGroup ? 2.5 : 1.5,
Fill = new SolidColorBrush(pathColors[i % pathColors.Length]) { Opacity = 0.1 }
};
for (int j = 0; j < dimensionCount; j++)
{
double angle = j * 2 * Math.PI / dimensionCount - Math.PI / 2;
double r = radius * scores[j] / 100;
double x = centerX + r * Math.Cos(angle);
double y = centerY + r * Math.Sin(angle);
polygon.Points.Add(new Point(x, y));
}
canvas.Children.Add(polygon);
// 数据点
for (int j = 0; j < dimensionCount; j++)
{
double angle = j * 2 * Math.PI / dimensionCount - Math.PI / 2;
double r = radius * scores[j] / 100;
double x = centerX + r * Math.Cos(angle);
double y = centerY + r * Math.Sin(angle);
var ellipse = new Ellipse
{
Width = 4,
Height = 4,
Fill = new SolidColorBrush(pathColors[i % pathColors.Length])
};
Canvas.SetLeft(ellipse, x - 2);
Canvas.SetTop(ellipse, y - 2);
canvas.Children.Add(ellipse);
}
}
}
private void DrawAxis(Canvas canvas, double left, double top, double width, double height)
{
// Y轴
var yAxis = new Line
{
X1 = left,
Y1 = top,
X2 = left,
Y2 = top + height,
Stroke = new SolidColorBrush(Colors.Gray),
StrokeThickness = 1
};
canvas.Children.Add(yAxis);
// X轴
var xAxis = new Line
{
X1 = left,
Y1 = top + height,
X2 = left + width,
Y2 = top + height,
Stroke = new SolidColorBrush(Colors.Gray),
StrokeThickness = 1
};
canvas.Children.Add(xAxis);
// Y轴刻度和标签
for (int i = 0; i <= 5; i++)
{
double y = top + height - i * height / 5;
var tick = new Line
{
X1 = left - 3,
Y1 = y,
X2 = left,
Y2 = y,
Stroke = new SolidColorBrush(Colors.Gray),
StrokeThickness = 1
};
canvas.Children.Add(tick);
var label = new TextBlock
{
Text = (i * 20).ToString(),
FontSize = 9,
Foreground = new SolidColorBrush(Colors.Gray)
};
Canvas.SetLeft(label, left - 20);
Canvas.SetTop(label, y - 6);
canvas.Children.Add(label);
}
}
private void DrawLegend(Canvas canvas, string[] names, Color[] colors, double x, double y)
{
for (int i = 0; i < names.Length; i++)
{
var rect = new Rectangle
{
Width = 10,
Height = 10,
Fill = new SolidColorBrush(colors[i]),
RadiusX = 2,
RadiusY = 2
};
Canvas.SetLeft(rect, x + i * 35);
Canvas.SetTop(rect, y);
canvas.Children.Add(rect);
var label = new TextBlock
{
Text = names[i],
FontSize = 9,
Foreground = new SolidColorBrush(Colors.Gray)
};
Canvas.SetLeft(label, x + i * 35 + 12);
Canvas.SetTop(label, y - 1);
canvas.Children.Add(label);
}
}
/// <summary> /// <summary>
/// 显示路径分析窗口(单例模式,非模态) /// 显示路径分析窗口(单例模式,非模态)
/// </summary> /// </summary>