using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Shapes;
using NavisworksTransport.Core;
using NavisworksTransport.Core.Models;
using NavisworksTransport.UI.WPF.ViewModels;
namespace NavisworksTransport.UI.WPF.Views
{
///
/// PathAnalysisDialog.xaml 的交互逻辑
/// 路径规划分析对话框 - 支持多维度评分和同终点组分析(非模态窗口)
///
public partial class PathAnalysisDialog : Window
{
#region 单例模式
private static PathAnalysisDialog _currentInstance = null;
private static readonly object _lock = new object();
#endregion
public PathAnalysisDialog()
{
try
{
InitializeComponent();
var viewModel = new ViewModels.PathAnalysisViewModel();
DataContext = viewModel;
// 订阅分析完成事件,绘制图表
viewModel.AnalysisCompleted += OnAnalysisCompleted;
LogManager.Info("PathAnalysisDialog初始化完成");
Title = $"路径规划分析 - 多路径对比 [{DateTime.Now:MM-dd HH:mm}]";
}
catch (Exception ex)
{
LogManager.Error($"初始化PathAnalysisDialog失败: {ex.Message}", ex);
}
}
///
/// 分析完成时绘制图表
///
private void OnAnalysisCompleted(object sender, AnalysisCompletedEventArgs e)
{
try
{
Dispatcher.Invoke(() =>
{
DrawBarChart(e.EndpointGroups);
DrawRadarChart(e.SelectedGroup);
});
}
catch (Exception ex)
{
LogManager.Error($"绘制图表失败: {ex.Message}");
}
}
///
/// 绘制柱状图
///
private void DrawBarChart(List groups)
{
if (groups == null || groups.Count == 0) return;
var canvas = BarChartCanvas;
canvas.Children.Clear();
// 收集所有路径数据
var allPaths = new List();
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);
}
///
/// 绘制雷达图
///
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);
}
}
///
/// 显示路径分析窗口(单例模式,非模态)
///
public static PathAnalysisDialog ShowAnalysis(Window owner = null)
{
try
{
lock (_lock)
{
// 如果当前已有实例且仍然打开,则激活窗口
if (_currentInstance != null)
{
try
{
if (_currentInstance.IsLoaded)
{
_currentInstance.Activate();
_currentInstance.Focus();
LogManager.Info("PathAnalysisDialog已激活现有窗口");
return _currentInstance;
}
}
catch (Exception)
{
// 如果访问失败,认为窗口已关闭,创建新实例
_currentInstance = null;
}
}
// 创建新实例
var dialog = new PathAnalysisDialog();
if (owner != null)
{
dialog.Owner = owner;
}
// 设置为当前实例
_currentInstance = dialog;
// 使用Show()非模态显示,允许与主窗口同时操作
dialog.Show();
LogManager.Info("PathAnalysisDialog已以非模态方式显示");
return dialog;
}
}
catch (Exception ex)
{
LogManager.Error($"显示路径分析窗口失败: {ex.Message}");
return null;
}
}
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
try
{
LogManager.Info("用户关闭路径分析对话框");
this.Close();
}
catch (Exception ex)
{
LogManager.Error($"关闭PathAnalysisDialog时发生错误: {ex.Message}", ex);
}
}
protected override void OnClosed(EventArgs e)
{
try
{
LogManager.Info("PathAnalysisDialog已关闭");
// 清理单例引用
lock (_lock)
{
if (_currentInstance == this)
{
_currentInstance = null;
}
}
base.OnClosed(e);
}
catch (Exception ex)
{
LogManager.Error($"PathAnalysisDialog清理过程中发生错误: {ex.Message}", ex);
}
}
}
#region 值转换器
///
/// 分数转颜色转换器
///
public class ScoreToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double score)
{
if (score >= 80) return new SolidColorBrush(Colors.Green);
if (score >= 60) return new SolidColorBrush(Colors.Orange);
return new SolidColorBrush(Colors.Red);
}
return new SolidColorBrush(Colors.Gray);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
///
/// 优先级转颜色转换器
///
public class PriorityToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is int priority)
{
if (priority >= 4) return new SolidColorBrush(Color.FromRgb(220, 53, 69)); // 红色 - 高优先级
if (priority >= 3) return new SolidColorBrush(Color.FromRgb(255, 193, 7)); // 黄色 - 中优先级
return new SolidColorBrush(Color.FromRgb(40, 167, 69)); // 绿色 - 低优先级
}
return new SolidColorBrush(Colors.Gray);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
///
/// 策略描述转换器
///
public class StrategyDescriptionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string strategy)
{
switch (strategy)
{
case "安全优先":
return "优先选择无碰撞路径,适合精密组件运输(安全权重50%)";
case "效率优先":
return "优先选择最短路径,适合紧急任务(效率权重40%)";
case "平衡模式":
return "综合考虑安全与效率,通用场景推荐(默认)";
default:
return "请选择分析策略";
}
}
return "";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
///
/// 布尔转背景色转换器
///
public class BoolToBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool isSelected && isSelected)
{
return new SolidColorBrush(Color.FromRgb(230, 240, 255)); // 选中时的浅蓝色
}
return new SolidColorBrush(Colors.Transparent);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
///
/// Null转Visibility转换器
///
public class NullToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value == null ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
#endregion
}