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 }