1601 lines
64 KiB
C#
1601 lines
64 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Drawing;
|
||
using System.Drawing.Drawing2D;
|
||
using System.Linq;
|
||
using System.Windows.Forms;
|
||
using Autodesk.Navisworks.Api;
|
||
using System.IO;
|
||
|
||
namespace NavisworksTransport
|
||
{
|
||
/// <summary>
|
||
/// 通道几何信息
|
||
/// </summary>
|
||
public class ChannelGeometry
|
||
{
|
||
/// <summary>
|
||
/// 通道模型项
|
||
/// </summary>
|
||
public ModelItem ChannelItem { get; set; }
|
||
|
||
/// <summary>
|
||
/// 通道名称
|
||
/// </summary>
|
||
public string Name { get; set; }
|
||
|
||
/// <summary>
|
||
/// 2D投影顶点集合(世界坐标)
|
||
/// </summary>
|
||
public List<Point3D> WorldVertices { get; set; }
|
||
|
||
/// <summary>
|
||
/// 2D投影顶点集合(地图坐标)
|
||
/// </summary>
|
||
public List<PointF> MapVertices { get; set; }
|
||
|
||
/// <summary>
|
||
/// 包围盒(用于快速绘制)
|
||
/// </summary>
|
||
public Rectangle BoundingRect { get; set; }
|
||
|
||
/// <summary>
|
||
/// 是否显示详细几何
|
||
/// </summary>
|
||
public bool ShowDetailedGeometry { get; set; }
|
||
|
||
/// <summary>
|
||
/// 是否显示顶点标签
|
||
/// </summary>
|
||
public bool ShowVertexLabels { get; set; }
|
||
|
||
public ChannelGeometry()
|
||
{
|
||
WorldVertices = new List<Point3D>();
|
||
MapVertices = new List<PointF>();
|
||
ShowDetailedGeometry = true;
|
||
ShowVertexLabels = true;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 2D导航地图窗口
|
||
/// 提供可视化的路径规划界面
|
||
/// </summary>
|
||
public partial class NavigationMapWindow : Form
|
||
{
|
||
private static string _logFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "NavisworksTransport_Debug.log");
|
||
|
||
/// <summary>
|
||
/// 写入调试日志(同时输出到Debug和文件)
|
||
/// </summary>
|
||
/// <param name="message">日志消息</param>
|
||
private static void WriteLog(string message)
|
||
{
|
||
var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}] {message}";
|
||
System.Diagnostics.Debug.WriteLine(logMessage);
|
||
|
||
try
|
||
{
|
||
File.AppendAllText(_logFilePath, logMessage + Environment.NewLine);
|
||
}
|
||
catch
|
||
{
|
||
// 忽略文件写入错误
|
||
}
|
||
}
|
||
|
||
private CoordinateConverter _coordinateConverter;
|
||
private PathRoute _currentRoute;
|
||
private List<ModelItem> _channelItems;
|
||
private List<Rectangle> _channelOutlines;
|
||
private List<ChannelGeometry> _channelGeometries;
|
||
private PathPlanningManager _pathPlanningManager;
|
||
|
||
// UI控件
|
||
private Panel _mapPanel;
|
||
private Label _coordinateLabel;
|
||
private TextBox _pointNameTextBox;
|
||
private ComboBox _pointTypeComboBox;
|
||
private Button _addPointButton;
|
||
private Button _removePointButton;
|
||
private Button _clearAllButton;
|
||
private Button _generatePathButton;
|
||
private ListBox _pointsListBox;
|
||
private GroupBox _coordinateGroupBox;
|
||
private TextBox _xCoordinateTextBox;
|
||
private TextBox _yCoordinateTextBox;
|
||
private TextBox _zCoordinateTextBox;
|
||
private Button _updateCoordinateButton;
|
||
|
||
// 绘制相关
|
||
private Bitmap _mapBuffer;
|
||
private System.Drawing.Graphics _mapGraphics;
|
||
private PathPoint _selectedPoint;
|
||
private bool _isDragging;
|
||
private System.Drawing.Point _lastMousePosition;
|
||
|
||
// 常量定义
|
||
private const int POINT_RADIUS = 6;
|
||
private const int POINT_SELECT_RADIUS = 10;
|
||
private readonly System.Drawing.Color START_POINT_COLOR = System.Drawing.Color.Green;
|
||
private readonly System.Drawing.Color END_POINT_COLOR = System.Drawing.Color.Red;
|
||
private readonly System.Drawing.Color WAY_POINT_COLOR = System.Drawing.Color.Blue;
|
||
private readonly System.Drawing.Color CHANNEL_COLOR = System.Drawing.Color.LightGray;
|
||
private readonly System.Drawing.Color PATH_LINE_COLOR = System.Drawing.Color.DarkBlue;
|
||
|
||
// 工具栏按钮
|
||
private System.Windows.Forms.Button btnZoomIn;
|
||
private System.Windows.Forms.Button btnZoomOut;
|
||
private System.Windows.Forms.Button btnResetView;
|
||
private System.Windows.Forms.Button btnClearPaths;
|
||
private System.Windows.Forms.Button btnValidatePath;
|
||
private System.Windows.Forms.Button btnOptimizePath;
|
||
private System.Windows.Forms.Button btnToggleGeometry;
|
||
private System.Windows.Forms.Button btnToggleLabels;
|
||
private System.Windows.Forms.Button btnRecalculateBounds;
|
||
|
||
// 状态栏
|
||
private System.Windows.Forms.StatusStrip statusStrip;
|
||
private System.Windows.Forms.ToolStripStatusLabel statusLabel;
|
||
|
||
public event EventHandler<PathRoute> PathGenerated;
|
||
public event EventHandler<PathPoint> PointSelected;
|
||
public event EventHandler<PathPoint> PointAdded;
|
||
public event EventHandler<PathPoint> PointRemoved;
|
||
|
||
/// <summary>
|
||
/// 当前路径
|
||
/// </summary>
|
||
public PathRoute CurrentRoute
|
||
{
|
||
get { return _currentRoute; }
|
||
set { _currentRoute = value; UpdatePointsList(); RedrawMap(); }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 构造函数
|
||
/// </summary>
|
||
/// <param name="coordinateConverter">坐标转换器</param>
|
||
/// <param name="channelItems">通道模型集合</param>
|
||
public NavigationMapWindow(CoordinateConverter coordinateConverter, List<ModelItem> channelItems)
|
||
{
|
||
_coordinateConverter = coordinateConverter ?? throw new ArgumentNullException(nameof(coordinateConverter));
|
||
_channelItems = channelItems ?? new List<ModelItem>();
|
||
_currentRoute = new PathRoute("新路径");
|
||
_channelOutlines = new List<Rectangle>();
|
||
_channelGeometries = new List<ChannelGeometry>();
|
||
_pathPlanningManager = new PathPlanningManager();
|
||
|
||
WriteLog($"=== NavigationMapWindow 初始化开始 ===");
|
||
WriteLog($"通道项数量: {_channelItems.Count}");
|
||
WriteLog($"地图尺寸: {coordinateConverter.MapWidth} x {coordinateConverter.MapHeight}");
|
||
WriteLog($"日志文件位置: {_logFilePath}");
|
||
|
||
InitializeComponent();
|
||
SetupEventHandlers();
|
||
GenerateChannelOutlines();
|
||
InitializeMapBuffer();
|
||
RedrawMap();
|
||
|
||
WriteLog($"=== NavigationMapWindow 初始化完成 ===");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化UI组件
|
||
/// </summary>
|
||
private void InitializeComponent()
|
||
{
|
||
this.Text = "路径规划地图";
|
||
this.Size = new Size(1000, 700);
|
||
this.StartPosition = FormStartPosition.CenterScreen;
|
||
this.FormBorderStyle = FormBorderStyle.Sizable;
|
||
this.MinimumSize = new Size(800, 600);
|
||
|
||
// 创建主布局
|
||
var mainLayout = new TableLayoutPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
ColumnCount = 2,
|
||
RowCount = 1
|
||
};
|
||
mainLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 70F));
|
||
mainLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 30F));
|
||
|
||
// 地图面板
|
||
_mapPanel = new Panel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
BackColor = System.Drawing.Color.White,
|
||
BorderStyle = BorderStyle.FixedSingle
|
||
};
|
||
|
||
// 控制面板
|
||
var controlPanel = CreateControlPanel();
|
||
|
||
// 创建工具栏按钮
|
||
btnZoomIn = new System.Windows.Forms.Button()
|
||
{
|
||
Text = "放大",
|
||
Size = new Size(60, 25),
|
||
Location = new Point(5, 5),
|
||
FlatStyle = FlatStyle.Flat
|
||
};
|
||
btnZoomIn.Click += (s, e) => { /* 放大功能 - 暂未实现 */ };
|
||
|
||
btnZoomOut = new System.Windows.Forms.Button()
|
||
{
|
||
Text = "缩小",
|
||
Size = new Size(60, 25),
|
||
Location = new Point(70, 5),
|
||
FlatStyle = FlatStyle.Flat
|
||
};
|
||
btnZoomOut.Click += (s, e) => { /* 缩小功能 - 暂未实现 */ };
|
||
|
||
btnResetView = new System.Windows.Forms.Button()
|
||
{
|
||
Text = "重置",
|
||
Size = new Size(60, 25),
|
||
Location = new Point(135, 5),
|
||
FlatStyle = FlatStyle.Flat
|
||
};
|
||
btnResetView.Click += (s, e) => { RedrawMap(); };
|
||
|
||
btnClearPaths = new System.Windows.Forms.Button()
|
||
{
|
||
Text = "清除路径",
|
||
Size = new Size(80, 25),
|
||
Location = new Point(200, 5),
|
||
BackColor = System.Drawing.Color.FromArgb(255, 128, 128),
|
||
FlatStyle = FlatStyle.Flat
|
||
};
|
||
btnClearPaths.Click += (s, e) => { _currentRoute?.Points.Clear(); RedrawMap(); };
|
||
|
||
btnValidatePath = new System.Windows.Forms.Button()
|
||
{
|
||
Text = "验证路径",
|
||
Size = new Size(80, 25),
|
||
Location = new Point(285, 5),
|
||
BackColor = System.Drawing.Color.FromArgb(255, 255, 128),
|
||
FlatStyle = FlatStyle.Flat
|
||
};
|
||
btnValidatePath.Click += BtnValidatePath_Click;
|
||
|
||
btnOptimizePath = new System.Windows.Forms.Button()
|
||
{
|
||
Text = "优化路径",
|
||
Size = new Size(80, 25),
|
||
Location = new Point(370, 5),
|
||
BackColor = System.Drawing.Color.FromArgb(128, 255, 128),
|
||
FlatStyle = FlatStyle.Flat
|
||
};
|
||
btnOptimizePath.Click += BtnOptimizePath_Click;
|
||
|
||
btnToggleGeometry = new System.Windows.Forms.Button()
|
||
{
|
||
Text = "详细几何",
|
||
Size = new Size(80, 25),
|
||
Location = new Point(455, 5),
|
||
BackColor = System.Drawing.Color.FromArgb(128, 200, 255),
|
||
FlatStyle = FlatStyle.Flat
|
||
};
|
||
btnToggleGeometry.Click += BtnToggleGeometry_Click;
|
||
|
||
btnToggleLabels = new System.Windows.Forms.Button()
|
||
{
|
||
Text = "坐标标注",
|
||
Size = new Size(80, 25),
|
||
Location = new Point(540, 5),
|
||
BackColor = System.Drawing.Color.FromArgb(255, 200, 128),
|
||
FlatStyle = FlatStyle.Flat
|
||
};
|
||
btnToggleLabels.Click += BtnToggleLabels_Click;
|
||
|
||
btnRecalculateBounds = new System.Windows.Forms.Button()
|
||
{
|
||
Text = "重算边界",
|
||
Size = new Size(80, 25),
|
||
Location = new Point(625, 5),
|
||
BackColor = System.Drawing.Color.FromArgb(255, 255, 128),
|
||
FlatStyle = FlatStyle.Flat
|
||
};
|
||
btnRecalculateBounds.Click += BtnRecalculateBounds_Click;
|
||
|
||
// 工具栏面板
|
||
var toolPanel = new Panel()
|
||
{
|
||
Dock = DockStyle.Top,
|
||
Height = 35,
|
||
BackColor = System.Drawing.Color.LightGray
|
||
};
|
||
|
||
toolPanel.Controls.Add(btnZoomIn);
|
||
toolPanel.Controls.Add(btnZoomOut);
|
||
toolPanel.Controls.Add(btnResetView);
|
||
toolPanel.Controls.Add(btnClearPaths);
|
||
toolPanel.Controls.Add(btnValidatePath);
|
||
toolPanel.Controls.Add(btnOptimizePath);
|
||
toolPanel.Controls.Add(btnToggleGeometry);
|
||
toolPanel.Controls.Add(btnToggleLabels);
|
||
toolPanel.Controls.Add(btnRecalculateBounds);
|
||
|
||
// 状态栏
|
||
statusStrip = new StatusStrip();
|
||
statusLabel = new ToolStripStatusLabel("就绪");
|
||
statusStrip.Items.Add(statusLabel);
|
||
|
||
mainLayout.Controls.Add(_mapPanel, 0, 0);
|
||
mainLayout.Controls.Add(controlPanel, 1, 0);
|
||
this.Controls.Add(toolPanel);
|
||
this.Controls.Add(mainLayout);
|
||
this.Controls.Add(statusStrip);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建控制面板
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private Panel CreateControlPanel()
|
||
{
|
||
var panel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(10) };
|
||
var layout = new TableLayoutPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
ColumnCount = 1,
|
||
RowCount = 6
|
||
};
|
||
|
||
// 点信息组
|
||
var pointInfoGroup = new GroupBox { Text = "路径点信息", Height = 120 };
|
||
var pointInfoLayout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 3 };
|
||
|
||
pointInfoLayout.Controls.Add(new Label { Text = "名称:", Anchor = AnchorStyles.Left }, 0, 0);
|
||
_pointNameTextBox = new TextBox { Dock = DockStyle.Fill };
|
||
pointInfoLayout.Controls.Add(_pointNameTextBox, 1, 0);
|
||
|
||
pointInfoLayout.Controls.Add(new Label { Text = "类型:", Anchor = AnchorStyles.Left }, 0, 1);
|
||
_pointTypeComboBox = new ComboBox
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
DropDownStyle = ComboBoxStyle.DropDownList
|
||
};
|
||
_pointTypeComboBox.Items.AddRange(new[] { "起点", "终点", "路径点" });
|
||
_pointTypeComboBox.SelectedIndex = 2;
|
||
pointInfoLayout.Controls.Add(_pointTypeComboBox, 1, 1);
|
||
|
||
var buttonLayout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 1 };
|
||
_addPointButton = new Button { Text = "添加点", Dock = DockStyle.Fill };
|
||
_removePointButton = new Button { Text = "删除点", Dock = DockStyle.Fill, Enabled = false };
|
||
buttonLayout.Controls.Add(_addPointButton, 0, 0);
|
||
buttonLayout.Controls.Add(_removePointButton, 1, 0);
|
||
pointInfoLayout.Controls.Add(buttonLayout, 0, 2);
|
||
pointInfoLayout.SetColumnSpan(buttonLayout, 2);
|
||
|
||
pointInfoGroup.Controls.Add(pointInfoLayout);
|
||
|
||
// 坐标编辑组
|
||
_coordinateGroupBox = new GroupBox { Text = "坐标编辑", Height = 140 };
|
||
var coordLayout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 4 };
|
||
|
||
coordLayout.Controls.Add(new Label { Text = "X:", Anchor = AnchorStyles.Left }, 0, 0);
|
||
_xCoordinateTextBox = new TextBox { Dock = DockStyle.Fill };
|
||
coordLayout.Controls.Add(_xCoordinateTextBox, 1, 0);
|
||
|
||
coordLayout.Controls.Add(new Label { Text = "Y:", Anchor = AnchorStyles.Left }, 0, 1);
|
||
_yCoordinateTextBox = new TextBox { Dock = DockStyle.Fill };
|
||
coordLayout.Controls.Add(_yCoordinateTextBox, 1, 1);
|
||
|
||
coordLayout.Controls.Add(new Label { Text = "Z:", Anchor = AnchorStyles.Left }, 0, 2);
|
||
_zCoordinateTextBox = new TextBox { Dock = DockStyle.Fill };
|
||
coordLayout.Controls.Add(_zCoordinateTextBox, 1, 2);
|
||
|
||
_updateCoordinateButton = new Button { Text = "更新坐标", Dock = DockStyle.Fill, Enabled = false };
|
||
coordLayout.Controls.Add(_updateCoordinateButton, 0, 3);
|
||
coordLayout.SetColumnSpan(_updateCoordinateButton, 2);
|
||
|
||
_coordinateGroupBox.Controls.Add(coordLayout);
|
||
|
||
// 路径点列表
|
||
var listGroup = new GroupBox { Text = "路径点列表", Height = 200 };
|
||
_pointsListBox = new ListBox { Dock = DockStyle.Fill };
|
||
listGroup.Controls.Add(_pointsListBox);
|
||
|
||
// 操作按钮
|
||
var actionLayout = new TableLayoutPanel { Height = 80, Dock = DockStyle.Top, ColumnCount = 1, RowCount = 2 };
|
||
_generatePathButton = new Button { Text = "生成路径", Dock = DockStyle.Fill };
|
||
_clearAllButton = new Button { Text = "清空所有", Dock = DockStyle.Fill };
|
||
actionLayout.Controls.Add(_generatePathButton, 0, 0);
|
||
actionLayout.Controls.Add(_clearAllButton, 0, 1);
|
||
|
||
// 状态标签
|
||
_coordinateLabel = new Label
|
||
{
|
||
Text = "点击地图添加路径点",
|
||
Dock = DockStyle.Bottom,
|
||
Height = 20,
|
||
ForeColor = System.Drawing.Color.DarkBlue
|
||
};
|
||
|
||
layout.Controls.Add(pointInfoGroup, 0, 0);
|
||
layout.Controls.Add(_coordinateGroupBox, 0, 1);
|
||
layout.Controls.Add(listGroup, 0, 2);
|
||
layout.Controls.Add(actionLayout, 0, 3);
|
||
layout.Controls.Add(_coordinateLabel, 0, 4);
|
||
|
||
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||
layout.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
|
||
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||
layout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
||
|
||
panel.Controls.Add(layout);
|
||
return panel;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置事件处理器
|
||
/// </summary>
|
||
private void SetupEventHandlers()
|
||
{
|
||
_mapPanel.Paint += MapPanel_Paint;
|
||
_mapPanel.MouseClick += MapPanel_MouseClick;
|
||
_mapPanel.MouseMove += MapPanel_MouseMove;
|
||
_mapPanel.MouseDown += MapPanel_MouseDown;
|
||
_mapPanel.MouseUp += MapPanel_MouseUp;
|
||
_mapPanel.Resize += MapPanel_Resize;
|
||
|
||
_addPointButton.Click += AddPointButton_Click;
|
||
_removePointButton.Click += RemovePointButton_Click;
|
||
_clearAllButton.Click += ClearAllButton_Click;
|
||
_generatePathButton.Click += GeneratePathButton_Click;
|
||
_updateCoordinateButton.Click += UpdateCoordinateButton_Click;
|
||
|
||
_pointsListBox.SelectedIndexChanged += PointsListBox_SelectedIndexChanged;
|
||
_pointNameTextBox.TextChanged += PointNameTextBox_TextChanged;
|
||
_pointTypeComboBox.SelectedIndexChanged += PointTypeComboBox_SelectedIndexChanged;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成通道轮廓
|
||
/// </summary>
|
||
private void GenerateChannelOutlines()
|
||
{
|
||
WriteLog($"=== 开始生成通道轮廓 ===");
|
||
_channelOutlines.Clear();
|
||
_channelGeometries.Clear();
|
||
|
||
foreach (var channel in _channelItems)
|
||
{
|
||
try
|
||
{
|
||
WriteLog($"处理通道: {channel.DisplayName}");
|
||
var boundingBox = channel.BoundingBox();
|
||
var channelBounds = new ChannelBounds(boundingBox);
|
||
MapPoint2D min2D, max2D;
|
||
channelBounds.Get2DProjection(out min2D, out max2D);
|
||
|
||
var minMap = _coordinateConverter.WorldToMap(new Autodesk.Navisworks.Api.Point3D(min2D.X, min2D.Y, 0));
|
||
var maxMap = _coordinateConverter.WorldToMap(new Autodesk.Navisworks.Api.Point3D(max2D.X, max2D.Y, 0));
|
||
|
||
var rect = new Rectangle(
|
||
(int)Math.Min(minMap.X, maxMap.X),
|
||
(int)Math.Min(minMap.Y, maxMap.Y),
|
||
(int)Math.Abs(maxMap.X - minMap.X),
|
||
(int)Math.Abs(maxMap.Y - minMap.Y)
|
||
);
|
||
|
||
WriteLog($"简单轮廓矩形: ({rect.X},{rect.Y},{rect.Width},{rect.Height})");
|
||
_channelOutlines.Add(rect);
|
||
|
||
// 创建详细几何信息
|
||
var geometry = CreateChannelGeometry(channel, boundingBox);
|
||
if (geometry != null)
|
||
{
|
||
_channelGeometries.Add(geometry);
|
||
WriteLog($"已添加几何: {geometry.Name}");
|
||
}
|
||
else
|
||
{
|
||
WriteLog($"几何创建失败: {channel.DisplayName}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"处理通道失败: {channel.DisplayName}, 错误: {ex.Message}");
|
||
continue;
|
||
}
|
||
}
|
||
|
||
WriteLog($"=== 通道轮廓生成完成,总数: {_channelGeometries.Count} ===");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建通道几何信息
|
||
/// </summary>
|
||
/// <param name="channel">通道模型项</param>
|
||
/// <param name="boundingBox">包围盒</param>
|
||
/// <returns>通道几何信息</returns>
|
||
private ChannelGeometry CreateChannelGeometry(ModelItem channel, BoundingBox3D boundingBox)
|
||
{
|
||
try
|
||
{
|
||
WriteLog($"=== 为通道创建几何: {channel.DisplayName} ===");
|
||
WriteLog($"包围盒: Min({boundingBox.Min.X:F2},{boundingBox.Min.Y:F2},{boundingBox.Min.Z:F2}) Max({boundingBox.Max.X:F2},{boundingBox.Max.Y:F2},{boundingBox.Max.Z:F2})");
|
||
|
||
var geometry = new ChannelGeometry
|
||
{
|
||
ChannelItem = channel,
|
||
Name = channel.DisplayName ?? "未命名通道"
|
||
};
|
||
|
||
// 从包围盒提取关键顶点(简化版,实际应该提取真实几何顶点)
|
||
var vertices = ExtractChannelVertices(boundingBox);
|
||
geometry.WorldVertices.AddRange(vertices);
|
||
WriteLog($"提取到 {vertices.Count} 个世界顶点");
|
||
|
||
// 转换为地图坐标
|
||
foreach (var worldVertex in vertices)
|
||
{
|
||
var mapPoint = _coordinateConverter.WorldToMap(worldVertex);
|
||
geometry.MapVertices.Add(new PointF((float)mapPoint.X, (float)mapPoint.Y));
|
||
WriteLog($"世界坐标 ({worldVertex.X:F2},{worldVertex.Y:F2}) -> 地图坐标 ({mapPoint.X:F2},{mapPoint.Y:F2})");
|
||
}
|
||
|
||
// 计算包围矩形
|
||
if (geometry.MapVertices.Count > 0)
|
||
{
|
||
var minX = geometry.MapVertices.Min(p => p.X);
|
||
var maxX = geometry.MapVertices.Max(p => p.X);
|
||
var minY = geometry.MapVertices.Min(p => p.Y);
|
||
var maxY = geometry.MapVertices.Max(p => p.Y);
|
||
|
||
geometry.BoundingRect = new Rectangle(
|
||
(int)minX, (int)minY,
|
||
(int)(maxX - minX), (int)(maxY - minY)
|
||
);
|
||
|
||
WriteLog($"计算的边界矩形: ({geometry.BoundingRect.X},{geometry.BoundingRect.Y},{geometry.BoundingRect.Width},{geometry.BoundingRect.Height})");
|
||
}
|
||
else
|
||
{
|
||
WriteLog("警告:没有有效的地图顶点");
|
||
}
|
||
|
||
// 默认启用详细几何显示和坐标标注
|
||
geometry.ShowDetailedGeometry = true;
|
||
geometry.ShowVertexLabels = true;
|
||
|
||
WriteLog($"=== 通道几何创建完成 ===");
|
||
return geometry;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"创建通道几何信息失败: {ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 提取通道顶点
|
||
/// </summary>
|
||
/// <param name="boundingBox">包围盒</param>
|
||
/// <returns>顶点集合</returns>
|
||
private List<Point3D> ExtractChannelVertices(BoundingBox3D boundingBox)
|
||
{
|
||
var vertices = new List<Point3D>();
|
||
|
||
// 检查包围盒尺寸是否合理
|
||
var width = boundingBox.Max.X - boundingBox.Min.X;
|
||
var length = boundingBox.Max.Y - boundingBox.Min.Y;
|
||
var height = boundingBox.Max.Z - boundingBox.Min.Z;
|
||
|
||
WriteLog($"通道原始尺寸 - 宽度:{width:F2}m, 长度:{length:F2}m, 高度:{height:F2}m");
|
||
|
||
// 如果包围盒过大,可能是坐标系统问题,进行限制
|
||
var maxDimension = Math.Max(width, Math.Max(length, height));
|
||
if (maxDimension > 1000) // 超过1公里
|
||
{
|
||
WriteLog($"警告:通道包围盒过大 - 宽度:{width:F2}m, 长度:{length:F2}m, 高度:{height:F2}m");
|
||
|
||
// 限制到合理范围(假设是100米范围内的通道)
|
||
var centerX = (boundingBox.Min.X + boundingBox.Max.X) / 2;
|
||
var centerY = (boundingBox.Min.Y + boundingBox.Max.Y) / 2;
|
||
var limitedSize = Math.Min(100, maxDimension / 100); // 限制在100米或原尺寸的1%
|
||
|
||
var halfSize = limitedSize / 2;
|
||
var minZ = boundingBox.Min.Z + 0.1;
|
||
|
||
vertices.Add(new Point3D(centerX - halfSize, centerY - halfSize, minZ)); // 左下
|
||
vertices.Add(new Point3D(centerX + halfSize, centerY - halfSize, minZ)); // 右下
|
||
vertices.Add(new Point3D(centerX + halfSize, centerY + halfSize, minZ)); // 右上
|
||
vertices.Add(new Point3D(centerX - halfSize, centerY + halfSize, minZ)); // 左上
|
||
|
||
WriteLog($"使用限制尺寸 {limitedSize:F2}m,中心点 ({centerX:F2},{centerY:F2})");
|
||
}
|
||
else
|
||
{
|
||
// 使用包围盒的四个角点作为基本顶点(俯视图)
|
||
var minZ = boundingBox.Min.Z + 0.1; // 稍微抬高以便可视化
|
||
|
||
vertices.Add(new Point3D(boundingBox.Min.X, boundingBox.Min.Y, minZ)); // 左下
|
||
vertices.Add(new Point3D(boundingBox.Max.X, boundingBox.Min.Y, minZ)); // 右下
|
||
vertices.Add(new Point3D(boundingBox.Max.X, boundingBox.Max.Y, minZ)); // 右上
|
||
vertices.Add(new Point3D(boundingBox.Min.X, boundingBox.Max.Y, minZ)); // 左上
|
||
|
||
WriteLog($"使用原始包围盒,Z={minZ:F2}");
|
||
}
|
||
|
||
WriteLog($"生成顶点数量: {vertices.Count}");
|
||
foreach (var vertex in vertices)
|
||
{
|
||
WriteLog($" 顶点: ({vertex.X:F2},{vertex.Y:F2},{vertex.Z:F2})");
|
||
}
|
||
|
||
return vertices;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化地图缓冲区
|
||
/// </summary>
|
||
private void InitializeMapBuffer()
|
||
{
|
||
if (_mapBuffer != null)
|
||
{
|
||
_mapBuffer.Dispose();
|
||
_mapGraphics?.Dispose();
|
||
}
|
||
|
||
_mapBuffer = new Bitmap(_mapPanel.Width, _mapPanel.Height);
|
||
_mapGraphics = System.Drawing.Graphics.FromImage(_mapBuffer);
|
||
_mapGraphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||
|
||
// 更新坐标转换器的地图尺寸
|
||
_coordinateConverter.UpdateMapSize(_mapPanel.Width, _mapPanel.Height);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重绘地图
|
||
/// </summary>
|
||
private void RedrawMap()
|
||
{
|
||
if (_mapGraphics == null) return;
|
||
|
||
// 清空背景
|
||
_mapGraphics.Clear(System.Drawing.Color.White);
|
||
|
||
// 绘制通道轮廓
|
||
DrawChannelOutlines();
|
||
|
||
// 绘制路径线
|
||
DrawPathLines();
|
||
|
||
// 绘制路径点
|
||
DrawPathPoints();
|
||
|
||
// 刷新显示
|
||
_mapPanel.Invalidate();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制通道轮廓
|
||
/// </summary>
|
||
private void DrawChannelOutlines()
|
||
{
|
||
// 绘制详细几何信息
|
||
foreach (var geometry in _channelGeometries)
|
||
{
|
||
DrawChannelGeometry(geometry);
|
||
}
|
||
|
||
// 如果没有详细几何信息,回退到简单矩形绘制
|
||
if (_channelGeometries.Count == 0)
|
||
{
|
||
using (var brush = new SolidBrush(CHANNEL_COLOR))
|
||
using (var pen = new Pen(System.Drawing.Color.Gray, 1))
|
||
{
|
||
foreach (var outline in _channelOutlines)
|
||
{
|
||
_mapGraphics.FillRectangle(brush, outline);
|
||
_mapGraphics.DrawRectangle(pen, outline);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制单个通道几何
|
||
/// </summary>
|
||
/// <param name="geometry">通道几何信息</param>
|
||
private void DrawChannelGeometry(ChannelGeometry geometry)
|
||
{
|
||
WriteLog($"准备绘制通道: {geometry.Name}, 地图顶点数: {geometry.MapVertices.Count}");
|
||
|
||
if (geometry.MapVertices.Count < 3)
|
||
{
|
||
WriteLog($"跳过绘制 {geometry.Name}: 顶点数不足 ({geometry.MapVertices.Count})");
|
||
|
||
// 即使顶点数不足,也尝试绘制边界矩形
|
||
if (geometry.BoundingRect.Width > 0 && geometry.BoundingRect.Height > 0)
|
||
{
|
||
WriteLog($"使用边界矩形绘制: ({geometry.BoundingRect.X},{geometry.BoundingRect.Y},{geometry.BoundingRect.Width},{geometry.BoundingRect.Height})");
|
||
using (var brush = new SolidBrush(System.Drawing.Color.FromArgb(128, CHANNEL_COLOR)))
|
||
using (var pen = new Pen(System.Drawing.Color.Gray, 2))
|
||
{
|
||
_mapGraphics.FillRectangle(brush, geometry.BoundingRect);
|
||
_mapGraphics.DrawRectangle(pen, geometry.BoundingRect);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
WriteLog($"开始绘制通道 {geometry.Name},详细几何: {geometry.ShowDetailedGeometry}");
|
||
|
||
// 绘制通道填充区域
|
||
using (var brush = new SolidBrush(System.Drawing.Color.FromArgb(128, CHANNEL_COLOR)))
|
||
using (var pen = new Pen(System.Drawing.Color.Gray, 2))
|
||
using (var vertexBrush = new SolidBrush(System.Drawing.Color.Red))
|
||
using (var labelFont = new Font("微软雅黑", 8))
|
||
using (var labelBrush = new SolidBrush(System.Drawing.Color.DarkBlue))
|
||
{
|
||
// 绘制多边形填充
|
||
if (geometry.ShowDetailedGeometry && geometry.MapVertices.Count >= 3)
|
||
{
|
||
_mapGraphics.FillPolygon(brush, geometry.MapVertices.ToArray());
|
||
_mapGraphics.DrawPolygon(pen, geometry.MapVertices.ToArray());
|
||
}
|
||
else
|
||
{
|
||
// 确保边界矩形有效
|
||
if (geometry.BoundingRect.Width > 0 && geometry.BoundingRect.Height > 0)
|
||
{
|
||
_mapGraphics.FillRectangle(brush, geometry.BoundingRect);
|
||
_mapGraphics.DrawRectangle(pen, geometry.BoundingRect);
|
||
}
|
||
else
|
||
{
|
||
// 回退方案:绘制一个基于顶点的简单矩形
|
||
if (geometry.MapVertices.Count >= 2)
|
||
{
|
||
var minX = geometry.MapVertices.Min(p => p.X);
|
||
var maxX = geometry.MapVertices.Max(p => p.X);
|
||
var minY = geometry.MapVertices.Min(p => p.Y);
|
||
var maxY = geometry.MapVertices.Max(p => p.Y);
|
||
|
||
var rect = new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
|
||
if (rect.Width > 0 && rect.Height > 0)
|
||
{
|
||
_mapGraphics.FillRectangle(brush, rect);
|
||
_mapGraphics.DrawRectangle(pen, rect);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 绘制顶点和坐标标注
|
||
if (geometry.ShowVertexLabels && geometry.ShowDetailedGeometry)
|
||
{
|
||
for (int i = 0; i < geometry.MapVertices.Count; i++)
|
||
{
|
||
var mapVertex = geometry.MapVertices[i];
|
||
var worldVertex = geometry.WorldVertices[i];
|
||
|
||
// 绘制顶点标记
|
||
var vertexRect = new RectangleF(mapVertex.X - 3, mapVertex.Y - 3, 6, 6);
|
||
_mapGraphics.FillEllipse(vertexBrush, vertexRect);
|
||
|
||
// 绘制坐标标签
|
||
var label = $"({worldVertex.X:F1},{worldVertex.Y:F1})";
|
||
var labelSize = _mapGraphics.MeasureString(label, labelFont);
|
||
|
||
// 计算标签位置,避免重叠
|
||
var labelX = mapVertex.X + 8;
|
||
var labelY = mapVertex.Y - labelSize.Height / 2;
|
||
|
||
// 如果标签超出边界,调整位置
|
||
if (labelX + labelSize.Width > _mapPanel.Width)
|
||
labelX = mapVertex.X - labelSize.Width - 8;
|
||
if (labelY < 0)
|
||
labelY = mapVertex.Y + 8;
|
||
if (labelY + labelSize.Height > _mapPanel.Height)
|
||
labelY = mapVertex.Y - labelSize.Height - 8;
|
||
|
||
// 绘制标签背景
|
||
var labelRect = new RectangleF(labelX - 2, labelY - 1, labelSize.Width + 4, labelSize.Height + 2);
|
||
_mapGraphics.FillRectangle(new SolidBrush(System.Drawing.Color.FromArgb(200, System.Drawing.Color.White)), labelRect);
|
||
_mapGraphics.DrawRectangle(new Pen(System.Drawing.Color.Gray, 1), Rectangle.Round(labelRect));
|
||
|
||
// 绘制坐标文本
|
||
_mapGraphics.DrawString(label, labelFont, labelBrush, labelX, labelY);
|
||
}
|
||
}
|
||
|
||
// 绘制通道名称
|
||
if (!string.IsNullOrEmpty(geometry.Name))
|
||
{
|
||
var centerX = geometry.MapVertices.Average(p => p.X);
|
||
var centerY = geometry.MapVertices.Average(p => p.Y);
|
||
|
||
using (var nameFont = new Font("微软雅黑", 10, FontStyle.Bold))
|
||
using (var nameBrush = new SolidBrush(System.Drawing.Color.DarkGreen))
|
||
{
|
||
var nameSize = _mapGraphics.MeasureString(geometry.Name, nameFont);
|
||
var nameX = centerX - nameSize.Width / 2;
|
||
var nameY = centerY - nameSize.Height / 2;
|
||
|
||
// 绘制名称背景
|
||
var nameRect = new RectangleF(nameX - 2, nameY - 1, nameSize.Width + 4, nameSize.Height + 2);
|
||
_mapGraphics.FillRectangle(new SolidBrush(System.Drawing.Color.FromArgb(180, System.Drawing.Color.Yellow)), nameRect);
|
||
|
||
// 绘制名称文本
|
||
_mapGraphics.DrawString(geometry.Name, nameFont, nameBrush, nameX, nameY);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"绘制通道几何失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制路径线
|
||
/// </summary>
|
||
private void DrawPathLines()
|
||
{
|
||
if (_currentRoute.Points.Count < 2) return;
|
||
|
||
var sortedPoints = _currentRoute.GetSortedPoints();
|
||
using (var pen = new Pen(PATH_LINE_COLOR, 2))
|
||
{
|
||
for (int i = 0; i < sortedPoints.Count - 1; i++)
|
||
{
|
||
var point1 = _coordinateConverter.WorldToMap(sortedPoints[i].Position);
|
||
var point2 = _coordinateConverter.WorldToMap(sortedPoints[i + 1].Position);
|
||
|
||
_mapGraphics.DrawLine(pen,
|
||
(float)point1.X, (float)point1.Y,
|
||
(float)point2.X, (float)point2.Y);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制路径点
|
||
/// </summary>
|
||
private void DrawPathPoints()
|
||
{
|
||
foreach (var point in _currentRoute.Points)
|
||
{
|
||
var mapPos = _coordinateConverter.WorldToMap(point.Position);
|
||
var color = GetPointColor(point.Type);
|
||
var isSelected = point == _selectedPoint;
|
||
|
||
using (var brush = new SolidBrush(color))
|
||
using (var pen = new Pen(isSelected ? System.Drawing.Color.Black : System.Drawing.Color.DarkGray, isSelected ? 2 : 1))
|
||
{
|
||
var rect = new Rectangle(
|
||
(int)mapPos.X - POINT_RADIUS,
|
||
(int)mapPos.Y - POINT_RADIUS,
|
||
POINT_RADIUS * 2,
|
||
POINT_RADIUS * 2
|
||
);
|
||
|
||
_mapGraphics.FillEllipse(brush, rect);
|
||
_mapGraphics.DrawEllipse(pen, rect);
|
||
}
|
||
|
||
// 绘制点名称
|
||
if (!string.IsNullOrEmpty(point.Name))
|
||
{
|
||
using (var font = new Font("微软雅黑", 8))
|
||
using (var brush = new SolidBrush(System.Drawing.Color.Black))
|
||
{
|
||
var textPos = new PointF((float)mapPos.X + POINT_RADIUS + 2, (float)mapPos.Y - POINT_RADIUS);
|
||
_mapGraphics.DrawString(point.Name, font, brush, textPos);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取路径点颜色
|
||
/// </summary>
|
||
/// <param name="type">路径点类型</param>
|
||
/// <returns>颜色</returns>
|
||
private System.Drawing.Color GetPointColor(PathPointType type)
|
||
{
|
||
switch (type)
|
||
{
|
||
case PathPointType.StartPoint: return START_POINT_COLOR;
|
||
case PathPointType.EndPoint: return END_POINT_COLOR;
|
||
case PathPointType.WayPoint: return WAY_POINT_COLOR;
|
||
default: return WAY_POINT_COLOR;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查找指定位置的路径点
|
||
/// </summary>
|
||
/// <param name="mapPosition">地图位置</param>
|
||
/// <returns>路径点,如果没有找到返回null</returns>
|
||
private PathPoint FindPointAtPosition(System.Drawing.Point mapPosition)
|
||
{
|
||
foreach (var point in _currentRoute.Points)
|
||
{
|
||
var pointMapPos = _coordinateConverter.WorldToMap(point.Position);
|
||
var distance = Math.Sqrt(
|
||
Math.Pow(mapPosition.X - pointMapPos.X, 2) +
|
||
Math.Pow(mapPosition.Y - pointMapPos.Y, 2)
|
||
);
|
||
|
||
if (distance <= POINT_SELECT_RADIUS)
|
||
{
|
||
return point;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新路径点列表显示
|
||
/// </summary>
|
||
private void UpdatePointsList()
|
||
{
|
||
_pointsListBox.Items.Clear();
|
||
var sortedPoints = _currentRoute.GetSortedPoints();
|
||
|
||
foreach (var point in sortedPoints)
|
||
{
|
||
_pointsListBox.Items.Add(point.ToString());
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新选中点的信息显示
|
||
/// </summary>
|
||
private void UpdateSelectedPointInfo()
|
||
{
|
||
if (_selectedPoint != null)
|
||
{
|
||
_pointNameTextBox.Text = _selectedPoint.Name;
|
||
_pointTypeComboBox.SelectedIndex = (int)_selectedPoint.Type;
|
||
_xCoordinateTextBox.Text = _selectedPoint.Position.X.ToString("F3");
|
||
_yCoordinateTextBox.Text = _selectedPoint.Position.Y.ToString("F3");
|
||
_zCoordinateTextBox.Text = _selectedPoint.Position.Z.ToString("F3");
|
||
|
||
_removePointButton.Enabled = true;
|
||
_updateCoordinateButton.Enabled = true;
|
||
_coordinateGroupBox.Enabled = true;
|
||
}
|
||
else
|
||
{
|
||
_pointNameTextBox.Text = "";
|
||
_pointTypeComboBox.SelectedIndex = 2;
|
||
_xCoordinateTextBox.Text = "";
|
||
_yCoordinateTextBox.Text = "";
|
||
_zCoordinateTextBox.Text = "";
|
||
|
||
_removePointButton.Enabled = false;
|
||
_updateCoordinateButton.Enabled = false;
|
||
_coordinateGroupBox.Enabled = false;
|
||
}
|
||
}
|
||
|
||
#region 事件处理器
|
||
|
||
private void MapPanel_Paint(object sender, PaintEventArgs e)
|
||
{
|
||
if (_mapBuffer != null)
|
||
{
|
||
e.Graphics.DrawImage(_mapBuffer, 0, 0);
|
||
}
|
||
|
||
// 绘制调试信息
|
||
DrawDebugInfo(e.Graphics);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 绘制调试信息
|
||
/// </summary>
|
||
/// <param name="graphics">绘图对象</param>
|
||
private void DrawDebugInfo(System.Drawing.Graphics graphics)
|
||
{
|
||
try
|
||
{
|
||
using (var font = new Font("Consolas", 9))
|
||
using (var brush = new SolidBrush(System.Drawing.Color.Black))
|
||
using (var bgBrush = new SolidBrush(System.Drawing.Color.FromArgb(200, System.Drawing.Color.White)))
|
||
{
|
||
var info = new List<string>();
|
||
|
||
// 坐标转换器信息
|
||
if (_coordinateConverter != null)
|
||
{
|
||
double scaleX, scaleY;
|
||
_coordinateConverter.GetMapScale(out scaleX, out scaleY);
|
||
info.Add($"地图尺寸: {_coordinateConverter.MapWidth:F0} x {_coordinateConverter.MapHeight:F0}");
|
||
info.Add($"世界范围: ({_coordinateConverter.ChannelBounds.MinPoint.X:F2},{_coordinateConverter.ChannelBounds.MinPoint.Y:F2}) - ({_coordinateConverter.ChannelBounds.MaxPoint.X:F2},{_coordinateConverter.ChannelBounds.MaxPoint.Y:F2})");
|
||
info.Add($"缩放比例: {scaleX:F6} x {scaleY:F6} (m/px)");
|
||
}
|
||
|
||
// 通道信息
|
||
info.Add($"通道数量: {_channelItems.Count}");
|
||
info.Add($"几何数量: {_channelGeometries.Count}");
|
||
|
||
// 显示第一个通道的详细信息
|
||
if (_channelGeometries.Count > 0)
|
||
{
|
||
var firstGeometry = _channelGeometries[0];
|
||
info.Add($"第一个通道: {firstGeometry.Name}");
|
||
info.Add($"世界顶点数: {firstGeometry.WorldVertices.Count}");
|
||
info.Add($"地图顶点数: {firstGeometry.MapVertices.Count}");
|
||
info.Add($"边界矩形: ({firstGeometry.BoundingRect.X},{firstGeometry.BoundingRect.Y},{firstGeometry.BoundingRect.Width},{firstGeometry.BoundingRect.Height})");
|
||
info.Add($"详细几何: {firstGeometry.ShowDetailedGeometry}");
|
||
info.Add($"显示标注: {firstGeometry.ShowVertexLabels}");
|
||
if (firstGeometry.WorldVertices.Count > 0)
|
||
{
|
||
var vertex = firstGeometry.WorldVertices[0];
|
||
info.Add($"世界顶点示例: ({vertex.X:F2},{vertex.Y:F2},{vertex.Z:F2})");
|
||
}
|
||
if (firstGeometry.MapVertices.Count > 0)
|
||
{
|
||
var mapVertex = firstGeometry.MapVertices[0];
|
||
info.Add($"地图顶点示例: ({mapVertex.X:F2},{mapVertex.Y:F2})");
|
||
}
|
||
}
|
||
|
||
// 路径信息
|
||
if (_currentRoute != null)
|
||
{
|
||
info.Add($"路径点数: {_currentRoute.Points.Count}");
|
||
info.Add($"路径长度: {_currentRoute.TotalLength:F3}m");
|
||
}
|
||
|
||
// 绘制信息框
|
||
var y = 10;
|
||
foreach (var line in info)
|
||
{
|
||
var size = graphics.MeasureString(line, font);
|
||
var rect = new RectangleF(10, y, size.Width + 4, size.Height + 2);
|
||
graphics.FillRectangle(bgBrush, rect);
|
||
graphics.DrawString(line, font, brush, 12, y + 1);
|
||
y += (int)size.Height + 2;
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"绘制调试信息失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void MapPanel_MouseClick(object sender, MouseEventArgs e)
|
||
{
|
||
if (e.Button == MouseButtons.Left)
|
||
{
|
||
var clickedPoint = FindPointAtPosition(e.Location);
|
||
|
||
if (clickedPoint != null)
|
||
{
|
||
// 选中现有点
|
||
_selectedPoint = clickedPoint;
|
||
UpdateSelectedPointInfo();
|
||
PointSelected?.Invoke(this, _selectedPoint);
|
||
}
|
||
else if (!_isDragging)
|
||
{
|
||
// 添加新点
|
||
AddPointAtPosition(e.Location);
|
||
}
|
||
|
||
RedrawMap();
|
||
}
|
||
}
|
||
|
||
private void MapPanel_MouseMove(object sender, MouseEventArgs e)
|
||
{
|
||
var mapPoint = new MapPoint2D(e.X, e.Y);
|
||
var worldPoint = _coordinateConverter.MapToWorld(mapPoint);
|
||
|
||
// 显示更详细的坐标信息
|
||
double scaleX, scaleY;
|
||
_coordinateConverter.GetMapScale(out scaleX, out scaleY);
|
||
_coordinateLabel.Text = $"坐标: X={worldPoint.X:F2}, Y={worldPoint.Y:F2}, Z={worldPoint.Z:F2} | 比例: {scaleX:F4}m/px";
|
||
|
||
if (_isDragging && _selectedPoint != null)
|
||
{
|
||
// 拖拽移动点
|
||
_selectedPoint.Position = worldPoint;
|
||
UpdateSelectedPointInfo();
|
||
RedrawMap();
|
||
}
|
||
}
|
||
|
||
private void MapPanel_MouseDown(object sender, MouseEventArgs e)
|
||
{
|
||
if (e.Button == MouseButtons.Left)
|
||
{
|
||
var clickedPoint = FindPointAtPosition(e.Location);
|
||
if (clickedPoint != null)
|
||
{
|
||
_isDragging = true;
|
||
_selectedPoint = clickedPoint;
|
||
_lastMousePosition = e.Location;
|
||
}
|
||
}
|
||
}
|
||
|
||
private void MapPanel_MouseUp(object sender, MouseEventArgs e)
|
||
{
|
||
_isDragging = false;
|
||
}
|
||
|
||
private void MapPanel_Resize(object sender, EventArgs e)
|
||
{
|
||
if (_mapPanel.Width > 0 && _mapPanel.Height > 0)
|
||
{
|
||
// 更新坐标转换器的地图尺寸
|
||
if (_coordinateConverter != null)
|
||
{
|
||
_coordinateConverter.UpdateMapSize(_mapPanel.Width, _mapPanel.Height);
|
||
}
|
||
|
||
InitializeMapBuffer();
|
||
GenerateChannelOutlines();
|
||
RedrawMap();
|
||
}
|
||
}
|
||
|
||
private void AddPointButton_Click(object sender, EventArgs e)
|
||
{
|
||
// 在地图中心添加点
|
||
var centerMap = new MapPoint2D(_mapPanel.Width / 2.0, _mapPanel.Height / 2.0);
|
||
AddPointAtPosition(new System.Drawing.Point((int)centerMap.X, (int)centerMap.Y));
|
||
}
|
||
|
||
private void RemovePointButton_Click(object sender, EventArgs e)
|
||
{
|
||
if (_selectedPoint != null)
|
||
{
|
||
_currentRoute.RemovePoint(_selectedPoint);
|
||
PointRemoved?.Invoke(this, _selectedPoint);
|
||
_selectedPoint = null;
|
||
UpdateSelectedPointInfo();
|
||
UpdatePointsList();
|
||
RedrawMap();
|
||
}
|
||
}
|
||
|
||
private void ClearAllButton_Click(object sender, EventArgs e)
|
||
{
|
||
if (MessageBox.Show("确定要清空所有路径点吗?", "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
|
||
{
|
||
_currentRoute.Points.Clear();
|
||
_selectedPoint = null;
|
||
UpdateSelectedPointInfo();
|
||
UpdatePointsList();
|
||
RedrawMap();
|
||
}
|
||
}
|
||
|
||
private void GeneratePathButton_Click(object sender, EventArgs e)
|
||
{
|
||
if (_currentRoute.IsValid())
|
||
{
|
||
PathGenerated?.Invoke(this, _currentRoute);
|
||
MessageBox.Show($"路径生成成功!\n总长度: {_currentRoute.TotalLength:F2}米", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
}
|
||
else
|
||
{
|
||
MessageBox.Show("路径无效!必须至少包含一个起点和一个终点。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
}
|
||
}
|
||
|
||
private void UpdateCoordinateButton_Click(object sender, EventArgs e)
|
||
{
|
||
if (_selectedPoint != null)
|
||
{
|
||
try
|
||
{
|
||
double x = double.Parse(_xCoordinateTextBox.Text);
|
||
double y = double.Parse(_yCoordinateTextBox.Text);
|
||
double z = double.Parse(_zCoordinateTextBox.Text);
|
||
|
||
_selectedPoint.Position = new Autodesk.Navisworks.Api.Point3D(x, y, z);
|
||
UpdatePointsList();
|
||
RedrawMap();
|
||
}
|
||
catch (FormatException)
|
||
{
|
||
MessageBox.Show("坐标格式错误,请输入有效的数字。", "错误", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void PointsListBox_SelectedIndexChanged(object sender, EventArgs e)
|
||
{
|
||
if (_pointsListBox.SelectedIndex >= 0)
|
||
{
|
||
var sortedPoints = _currentRoute.GetSortedPoints();
|
||
_selectedPoint = sortedPoints[_pointsListBox.SelectedIndex];
|
||
UpdateSelectedPointInfo();
|
||
RedrawMap();
|
||
}
|
||
}
|
||
|
||
private void PointNameTextBox_TextChanged(object sender, EventArgs e)
|
||
{
|
||
if (_selectedPoint != null)
|
||
{
|
||
_selectedPoint.Name = _pointNameTextBox.Text;
|
||
UpdatePointsList();
|
||
RedrawMap();
|
||
}
|
||
}
|
||
|
||
private void PointTypeComboBox_SelectedIndexChanged(object sender, EventArgs e)
|
||
{
|
||
if (_selectedPoint != null)
|
||
{
|
||
_selectedPoint.Type = (PathPointType)_pointTypeComboBox.SelectedIndex;
|
||
UpdatePointsList();
|
||
RedrawMap();
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 在指定位置添加路径点
|
||
/// </summary>
|
||
/// <param name="position">地图位置</param>
|
||
private void AddPointAtPosition(System.Drawing.Point position)
|
||
{
|
||
var mapPoint = new MapPoint2D(position.X, position.Y);
|
||
var worldPoint = _coordinateConverter.MapToWorld(mapPoint);
|
||
|
||
var pointType = (PathPointType)_pointTypeComboBox.SelectedIndex;
|
||
var pointName = string.IsNullOrEmpty(_pointNameTextBox.Text) ?
|
||
$"{GetPointTypeName(pointType)}{_currentRoute.Points.Count + 1}" :
|
||
_pointNameTextBox.Text;
|
||
|
||
var newPoint = new PathPoint(worldPoint, pointName, pointType);
|
||
_currentRoute.AddPoint(newPoint);
|
||
|
||
_selectedPoint = newPoint;
|
||
UpdateSelectedPointInfo();
|
||
UpdatePointsList();
|
||
PointAdded?.Invoke(this, newPoint);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取路径点类型名称
|
||
/// </summary>
|
||
/// <param name="type">路径点类型</param>
|
||
/// <returns>类型名称</returns>
|
||
private string GetPointTypeName(PathPointType type)
|
||
{
|
||
switch (type)
|
||
{
|
||
case PathPointType.StartPoint: return "起点";
|
||
case PathPointType.EndPoint: return "终点";
|
||
case PathPointType.WayPoint: return "路径点";
|
||
default: return "点";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放资源
|
||
/// </summary>
|
||
protected override void Dispose(bool disposing)
|
||
{
|
||
if (disposing)
|
||
{
|
||
_mapBuffer?.Dispose();
|
||
_mapGraphics?.Dispose();
|
||
}
|
||
base.Dispose(disposing);
|
||
}
|
||
|
||
#region 路径验证与优化
|
||
|
||
/// <summary>
|
||
/// 验证路径按钮点击事件
|
||
/// </summary>
|
||
private void BtnValidatePath_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_currentRoute == null || _currentRoute.Points.Count == 0)
|
||
{
|
||
statusLabel.Text = "当前没有可验证的路径";
|
||
MessageBox.Show("请先创建路径点", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
// 显示验证进度
|
||
statusLabel.Text = "正在验证路径...";
|
||
System.Windows.Forms.Application.DoEvents();
|
||
|
||
// 执行路径验证
|
||
var validationResult = _pathPlanningManager.ValidatePath(_currentRoute);
|
||
|
||
// 显示验证结果
|
||
statusLabel.Text = validationResult.IsValid ? "路径验证通过" : "路径验证失败";
|
||
|
||
var resultDialog = new PathValidationResultDialog(validationResult);
|
||
resultDialog.ShowDialog(this);
|
||
|
||
// 如果验证失败,在地图上高亮显示问题区域
|
||
if (!validationResult.IsValid)
|
||
{
|
||
HighlightValidationIssues(validationResult);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
statusLabel.Text = "路径验证失败";
|
||
MessageBox.Show($"路径验证时发生错误:{ex.Message}", "错误",
|
||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 优化路径按钮点击事件
|
||
/// </summary>
|
||
private void BtnOptimizePath_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_currentRoute == null || _currentRoute.Points.Count == 0)
|
||
{
|
||
statusLabel.Text = "当前没有可优化的路径";
|
||
MessageBox.Show("请先创建路径点", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
// 显示优化选项对话框
|
||
var optionsDialog = new PathOptimizationOptionsDialog();
|
||
if (optionsDialog.ShowDialog(this) != DialogResult.OK)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// 显示优化进度
|
||
statusLabel.Text = "正在优化路径...";
|
||
System.Windows.Forms.Application.DoEvents();
|
||
|
||
// 执行路径优化
|
||
var optimizationResult = _pathPlanningManager.OptimizePath(_currentRoute, optionsDialog.SelectedOptions);
|
||
|
||
// 显示优化结果
|
||
if (optimizationResult.Success)
|
||
{
|
||
// 更新当前路径为优化后的路径
|
||
_currentRoute = optimizationResult.OptimizedRoute;
|
||
|
||
// 重新绘制地图
|
||
_mapPanel.Invalidate();
|
||
|
||
statusLabel.Text = $"路径优化完成 - 长度减少{optimizationResult.LengthReduction:F3}m";
|
||
|
||
var resultDialog = new PathOptimizationResultDialog(optimizationResult);
|
||
resultDialog.ShowDialog(this);
|
||
}
|
||
else
|
||
{
|
||
statusLabel.Text = "路径优化失败";
|
||
MessageBox.Show($"路径优化失败:{optimizationResult.Message}", "错误",
|
||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
statusLabel.Text = "路径优化失败";
|
||
MessageBox.Show($"路径优化时发生错误:{ex.Message}", "错误",
|
||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在地图上高亮显示验证问题区域
|
||
/// </summary>
|
||
/// <param name="validationResult">验证结果</param>
|
||
private void HighlightValidationIssues(PathValidationResult validationResult)
|
||
{
|
||
// 简化实现:重新绘制地图,使用不同颜色标识有问题的路径段
|
||
_mapPanel.Invalidate();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换详细几何显示
|
||
/// </summary>
|
||
private void BtnToggleGeometry_Click(object sender, EventArgs e)
|
||
{
|
||
foreach (var geometry in _channelGeometries)
|
||
{
|
||
geometry.ShowDetailedGeometry = !geometry.ShowDetailedGeometry;
|
||
}
|
||
|
||
// 更新按钮文本
|
||
var anyDetailedShown = _channelGeometries.Any(g => g.ShowDetailedGeometry);
|
||
btnToggleGeometry.Text = anyDetailedShown ? "简化显示" : "详细几何";
|
||
|
||
RedrawMap();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 切换坐标标注显示
|
||
/// </summary>
|
||
private void BtnToggleLabels_Click(object sender, EventArgs e)
|
||
{
|
||
foreach (var geometry in _channelGeometries)
|
||
{
|
||
geometry.ShowVertexLabels = !geometry.ShowVertexLabels;
|
||
}
|
||
|
||
// 更新按钮文本
|
||
var anyLabelsShown = _channelGeometries.Any(g => g.ShowVertexLabels);
|
||
btnToggleLabels.Text = anyLabelsShown ? "隐藏坐标" : "坐标标注";
|
||
|
||
RedrawMap();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重新计算边界按钮点击事件
|
||
/// </summary>
|
||
private void BtnRecalculateBounds_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 重新生成通道轮廓
|
||
GenerateChannelOutlines();
|
||
|
||
// 如果有PathPlanningManager,重新计算其边界
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
// 设置选中的通道为活动通道,这会触发边界重新计算
|
||
var channelCollection = new ModelItemCollection();
|
||
foreach (var channel in _channelItems)
|
||
{
|
||
channelCollection.Add(channel);
|
||
}
|
||
_pathPlanningManager.SetActiveChannels(channelCollection);
|
||
}
|
||
|
||
// 更新坐标转换器的地图尺寸(以防窗口已调整大小)
|
||
if (_coordinateConverter != null)
|
||
{
|
||
_coordinateConverter.UpdateMapSize(_mapPanel.Width, _mapPanel.Height);
|
||
}
|
||
|
||
// 重新绘制地图
|
||
RedrawMap();
|
||
|
||
statusLabel.Text = "边界已重新计算,通道设置已更新";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
statusLabel.Text = "重新计算边界失败";
|
||
System.Diagnostics.Debug.WriteLine($"重新计算边界失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 辅助对话框类
|
||
|
||
/// <summary>
|
||
/// 路径优化选项对话框(简化实现)
|
||
/// </summary>
|
||
private class PathOptimizationOptionsDialog : Form
|
||
{
|
||
public PathOptimizationOptions SelectedOptions { get; private set; }
|
||
private CheckBox chkRemoveDuplicates;
|
||
private CheckBox chkSmoothPath;
|
||
private CheckBox chkOptimizeAngles;
|
||
private CheckBox chkAdjustToChannels;
|
||
|
||
public PathOptimizationOptionsDialog()
|
||
{
|
||
InitializeDialog();
|
||
SelectedOptions = PathOptimizationOptions.CreateDefault();
|
||
}
|
||
|
||
private void InitializeDialog()
|
||
{
|
||
Text = "路径优化选项";
|
||
Size = new Size(300, 200);
|
||
StartPosition = FormStartPosition.CenterParent;
|
||
|
||
chkRemoveDuplicates = new CheckBox { Text = "移除重复点", Location = new Point(10, 10), Checked = true };
|
||
chkSmoothPath = new CheckBox { Text = "路径平滑", Location = new Point(10, 40), Checked = true };
|
||
chkOptimizeAngles = new CheckBox { Text = "优化角度", Location = new Point(10, 70), Checked = true };
|
||
chkAdjustToChannels = new CheckBox { Text = "调整到通道中心", Location = new Point(10, 100), Checked = false };
|
||
|
||
var btnOK = new Button { Text = "确定", Location = new Point(110, 130), DialogResult = DialogResult.OK };
|
||
var btnCancel = new Button { Text = "取消", Location = new Point(190, 130), DialogResult = DialogResult.Cancel };
|
||
|
||
btnOK.Click += (s, e) => {
|
||
SelectedOptions.RemoveDuplicatePoints = chkRemoveDuplicates.Checked;
|
||
SelectedOptions.SmoothPath = chkSmoothPath.Checked;
|
||
SelectedOptions.OptimizeAngles = chkOptimizeAngles.Checked;
|
||
SelectedOptions.AdjustToChannels = chkAdjustToChannels.Checked;
|
||
};
|
||
|
||
Controls.AddRange(new Control[] { chkRemoveDuplicates, chkSmoothPath, chkOptimizeAngles, chkAdjustToChannels, btnOK, btnCancel });
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 路径验证结果对话框(简化实现)
|
||
/// </summary>
|
||
private class PathValidationResultDialog : Form
|
||
{
|
||
public PathValidationResultDialog(PathValidationResult result)
|
||
{
|
||
Text = "路径验证结果";
|
||
Size = new Size(400, 300);
|
||
StartPosition = FormStartPosition.CenterParent;
|
||
|
||
var textBox = new TextBox
|
||
{
|
||
Multiline = true,
|
||
ScrollBars = ScrollBars.Vertical,
|
||
ReadOnly = true,
|
||
Dock = DockStyle.Fill,
|
||
Text = result.GetDetailedReport()
|
||
};
|
||
|
||
var btnClose = new Button
|
||
{
|
||
Text = "关闭",
|
||
Dock = DockStyle.Bottom,
|
||
Height = 30,
|
||
DialogResult = DialogResult.OK
|
||
};
|
||
|
||
Controls.Add(textBox);
|
||
Controls.Add(btnClose);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 路径优化结果对话框(简化实现)
|
||
/// </summary>
|
||
private class PathOptimizationResultDialog : Form
|
||
{
|
||
public PathOptimizationResultDialog(PathOptimizationResult result)
|
||
{
|
||
Text = "路径优化结果";
|
||
Size = new Size(400, 300);
|
||
StartPosition = FormStartPosition.CenterParent;
|
||
|
||
var textBox = new TextBox
|
||
{
|
||
Multiline = true,
|
||
ScrollBars = ScrollBars.Vertical,
|
||
ReadOnly = true,
|
||
Dock = DockStyle.Fill,
|
||
Text = result.GetDetailedReport()
|
||
};
|
||
|
||
var btnClose = new Button
|
||
{
|
||
Text = "关闭",
|
||
Dock = DockStyle.Bottom,
|
||
Height = 30,
|
||
DialogResult = DialogResult.OK
|
||
};
|
||
|
||
Controls.Add(textBox);
|
||
Controls.Add(btnClose);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |