2552 lines
108 KiB
C#
2552 lines
108 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>
|
||
/// 清空调试日志
|
||
/// </summary>
|
||
private static void ClearLog()
|
||
{
|
||
try
|
||
{
|
||
if (File.Exists(_logFilePath))
|
||
{
|
||
File.Delete(_logFilePath);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 忽略文件删除错误
|
||
}
|
||
}
|
||
|
||
/// <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;
|
||
|
||
// 新增:3D交互控制按钮
|
||
// 类别设置控件
|
||
private System.Windows.Forms.ComboBox cmbCategoryType;
|
||
private System.Windows.Forms.Button btnSetCategory;
|
||
|
||
// 3D交互模式控件
|
||
private System.Windows.Forms.Button btnEnterPathEditMode;
|
||
private System.Windows.Forms.Button btnExitPathEditMode;
|
||
private System.Windows.Forms.Button btnHighlightChannels;
|
||
private System.Windows.Forms.Button btnClearHighlight;
|
||
private System.Windows.Forms.Button btnOpen2DMap;
|
||
private System.Windows.Forms.ComboBox cmbPointType;
|
||
private System.Windows.Forms.Label lblCurrentMode;
|
||
|
||
// 3D交互相关
|
||
private PathPlanningManager _pathManager;
|
||
private bool _isListeningFor3DClicks = false;
|
||
|
||
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)
|
||
{
|
||
// 首先清空日志
|
||
ClearLog();
|
||
|
||
_coordinateConverter = coordinateConverter ?? throw new ArgumentNullException(nameof(coordinateConverter));
|
||
_channelItems = channelItems ?? new List<ModelItem>();
|
||
_currentRoute = new PathRoute("新路径");
|
||
_channelOutlines = new List<Rectangle>();
|
||
_channelGeometries = new List<ChannelGeometry>();
|
||
// 创建PathPlanningManager并设置通道
|
||
_pathPlanningManager = new PathPlanningManager();
|
||
if (_channelItems.Count > 0)
|
||
{
|
||
// 将通道设置为选中状态
|
||
var channelCollection = new ModelItemCollection();
|
||
foreach (var channel in _channelItems)
|
||
{
|
||
channelCollection.Add(channel);
|
||
}
|
||
_pathPlanningManager.SetActiveChannels(channelCollection);
|
||
WriteLog($"已设置 {_channelItems.Count} 个通道到PathPlanningManager");
|
||
}
|
||
|
||
WriteLog($"=== NavigationMapWindow 初始化开始 ===");
|
||
WriteLog($"通道项数量: {_channelItems.Count}");
|
||
WriteLog($"地图尺寸: {coordinateConverter.MapWidth} x {coordinateConverter.MapHeight}");
|
||
WriteLog($"日志文件位置: {_logFilePath}");
|
||
|
||
InitializeComponent();
|
||
SetupEventHandlers();
|
||
|
||
// 延迟初始化到窗口显示时
|
||
this.Load += NavigationMapWindow_Load;
|
||
this.Shown += NavigationMapWindow_Shown;
|
||
|
||
WriteLog($"=== NavigationMapWindow 初始化完成 ===");
|
||
|
||
// 确保3D交互按钮可见(调试用)
|
||
EnsureButtonsVisible();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 窗口加载事件处理
|
||
/// </summary>
|
||
private void NavigationMapWindow_Load(object sender, EventArgs e)
|
||
{
|
||
WriteLog($"=== 窗口Load事件 - 地图面板尺寸: {_mapPanel.Width} x {_mapPanel.Height} ===");
|
||
|
||
// 确保地图面板有正确的尺寸后再初始化
|
||
if (_mapPanel.Width > 0 && _mapPanel.Height > 0)
|
||
{
|
||
InitializeMapContent();
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 窗口显示事件处理
|
||
/// </summary>
|
||
private void NavigationMapWindow_Shown(object sender, EventArgs e)
|
||
{
|
||
WriteLog($"=== 窗口Shown事件 - 地图面板尺寸: {_mapPanel.Width} x {_mapPanel.Height} ===");
|
||
|
||
// 如果在Load事件中没有初始化(可能尺寸还没设置),在这里再次尝试
|
||
if (_mapBuffer == null && _mapPanel.Width > 0 && _mapPanel.Height > 0)
|
||
{
|
||
InitializeMapContent();
|
||
}
|
||
|
||
// 强制重绘
|
||
ForceRedraw();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化地图内容
|
||
/// </summary>
|
||
private void InitializeMapContent()
|
||
{
|
||
WriteLog($"=== 开始初始化地图内容 ===");
|
||
|
||
// 更新坐标转换器的地图尺寸
|
||
_coordinateConverter.UpdateMapSize(_mapPanel.Width, _mapPanel.Height);
|
||
WriteLog($"更新坐标转换器尺寸: {_mapPanel.Width} x {_mapPanel.Height}");
|
||
|
||
// 初始化地图缓冲区
|
||
InitializeMapBuffer();
|
||
|
||
// 生成通道轮廓
|
||
GenerateChannelOutlines();
|
||
|
||
// 根据实际通道几何重新计算坐标转换器范围
|
||
RecalculateCoordinateConverterBounds();
|
||
|
||
// 绘制地图
|
||
RedrawMap();
|
||
|
||
WriteLog($"=== 地图内容初始化完成 ===");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 强制重绘地图
|
||
/// </summary>
|
||
private void ForceRedraw()
|
||
{
|
||
WriteLog($"=== 强制重绘地图 ===");
|
||
|
||
// 重新绘制地图缓冲区
|
||
RedrawMap();
|
||
|
||
// 刷新面板显示
|
||
_mapPanel.Invalidate();
|
||
_mapPanel.Update();
|
||
|
||
// 强制重绘
|
||
this.Refresh();
|
||
|
||
WriteLog($"=== 强制重绘完成 ===");
|
||
}
|
||
|
||
/// <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(5),
|
||
AutoScroll = true, // 添加自动滚动
|
||
BackColor = System.Drawing.Color.LightYellow // 添加背景色便于调试
|
||
};
|
||
var layout = new TableLayoutPanel
|
||
{
|
||
Dock = DockStyle.Fill,
|
||
ColumnCount = 1,
|
||
RowCount = 7,
|
||
AutoSize = true // 添加自动尺寸
|
||
};
|
||
|
||
// 类别设置组(改为下拉框,放在最上面但占用更少空间)
|
||
var categoryGroup = new GroupBox
|
||
{
|
||
Text = "★★★ 类别设置 ★★★",
|
||
Height = 90,
|
||
Dock = DockStyle.Top,
|
||
BackColor = System.Drawing.Color.LightGreen, // 使用绿色背景便于识别
|
||
Font = new Font("Microsoft Sans Serif", 9, FontStyle.Bold)
|
||
};
|
||
var categoryLayout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 2 };
|
||
|
||
categoryLayout.Controls.Add(new Label { Text = "设为类型:", Anchor = AnchorStyles.Left }, 0, 0);
|
||
cmbCategoryType = new ComboBox { Dock = DockStyle.Fill, DropDownStyle = ComboBoxStyle.DropDownList };
|
||
cmbCategoryType.Items.AddRange(new[] { "门", "电梯", "楼梯", "通道", "障碍物", "装卸区", "停车位", "检查点" });
|
||
cmbCategoryType.SelectedIndex = 3; // 默认选择"通道"
|
||
categoryLayout.Controls.Add(cmbCategoryType, 1, 0);
|
||
|
||
btnSetCategory = new Button
|
||
{
|
||
Text = "★应用类别★",
|
||
Dock = DockStyle.Fill,
|
||
BackColor = System.Drawing.Color.Orange,
|
||
Font = new Font("Microsoft Sans Serif", 9, FontStyle.Bold)
|
||
};
|
||
categoryLayout.Controls.Add(btnSetCategory, 0, 1);
|
||
categoryLayout.SetColumnSpan(btnSetCategory, 2);
|
||
|
||
categoryGroup.Controls.Add(categoryLayout);
|
||
|
||
// 3D交互模式控制组(放在第二位,确保可见)
|
||
var modeGroup = new GroupBox
|
||
{
|
||
Text = "★★★ 3D交互模式 ★★★",
|
||
Height = 180,
|
||
Dock = DockStyle.Top,
|
||
BackColor = System.Drawing.Color.Red, // 使用红色背景,更容易看到
|
||
Font = new Font("Microsoft Sans Serif", 10, FontStyle.Bold)
|
||
};
|
||
var modeLayout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 4 };
|
||
|
||
btnEnterPathEditMode = new Button { Text = "★进入路径编辑★", Dock = DockStyle.Fill, BackColor = System.Drawing.Color.LightGreen, Font = new Font("Microsoft Sans Serif", 9, FontStyle.Bold) };
|
||
btnExitPathEditMode = new Button { Text = "★退出路径编辑★", Dock = DockStyle.Fill, BackColor = System.Drawing.Color.LightCoral, Font = new Font("Microsoft Sans Serif", 9, FontStyle.Bold) };
|
||
modeLayout.Controls.Add(btnEnterPathEditMode, 0, 0);
|
||
modeLayout.Controls.Add(btnExitPathEditMode, 1, 0);
|
||
|
||
btnHighlightChannels = new Button { Text = "高亮通道", Dock = DockStyle.Fill };
|
||
btnClearHighlight = new Button { Text = "清除高亮", Dock = DockStyle.Fill };
|
||
modeLayout.Controls.Add(btnHighlightChannels, 0, 1);
|
||
modeLayout.Controls.Add(btnClearHighlight, 1, 1);
|
||
|
||
modeLayout.Controls.Add(new Label { Text = "点类型:", Anchor = AnchorStyles.Left }, 0, 2);
|
||
cmbPointType = new ComboBox { Dock = DockStyle.Fill, DropDownStyle = ComboBoxStyle.DropDownList };
|
||
cmbPointType.Items.AddRange(new[] { "起点", "终点", "路径点" });
|
||
cmbPointType.SelectedIndex = 2;
|
||
modeLayout.Controls.Add(cmbPointType, 1, 2);
|
||
|
||
lblCurrentMode = new Label
|
||
{
|
||
Text = "当前模式: 2D地图模式",
|
||
Dock = DockStyle.Fill,
|
||
Font = new Font("Microsoft Sans Serif", 8, FontStyle.Bold),
|
||
ForeColor = System.Drawing.Color.DarkBlue
|
||
};
|
||
modeLayout.Controls.Add(lblCurrentMode, 0, 3);
|
||
modeLayout.SetColumnSpan(lblCurrentMode, 2);
|
||
|
||
modeGroup.Controls.Add(modeLayout);
|
||
|
||
// 2D地图访问按钮
|
||
btnOpen2DMap = new Button
|
||
{
|
||
Text = "打开2D导航地图",
|
||
Height = 35,
|
||
Dock = DockStyle.Top,
|
||
BackColor = System.Drawing.Color.LightYellow,
|
||
Font = new Font("Microsoft Sans Serif", 9, FontStyle.Bold)
|
||
};
|
||
|
||
// 点信息组
|
||
var pointInfoGroup = new GroupBox { Text = "路径点信息", Height = 120, Dock = DockStyle.Top };
|
||
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);
|
||
|
||
|
||
|
||
// 路径点列表
|
||
var listGroup = new GroupBox { Text = "路径点列表", Height = 200, Dock = DockStyle.Top };
|
||
_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(categoryGroup, 0, 0);
|
||
layout.Controls.Add(modeGroup, 0, 1);
|
||
layout.Controls.Add(btnOpen2DMap, 0, 2);
|
||
layout.Controls.Add(pointInfoGroup, 0, 3);
|
||
layout.Controls.Add(listGroup, 0, 4);
|
||
layout.Controls.Add(actionLayout, 0, 5);
|
||
layout.Controls.Add(_coordinateLabel, 0, 6);
|
||
|
||
// 设置行样式,确保有足够的空间显示所有控件
|
||
layout.RowStyles.Add(new RowStyle(SizeType.Absolute, 90F)); // 类别设置组
|
||
layout.RowStyles.Add(new RowStyle(SizeType.Absolute, 180F)); // 3D交互模式组
|
||
layout.RowStyles.Add(new RowStyle(SizeType.Absolute, 40F)); // 2D地图按钮
|
||
layout.RowStyles.Add(new RowStyle(SizeType.Absolute, 120F)); // 点信息组
|
||
layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); // 路径点列表
|
||
layout.RowStyles.Add(new RowStyle(SizeType.Absolute, 80F)); // 操作按钮
|
||
layout.RowStyles.Add(new RowStyle(SizeType.Absolute, 30F)); // 状态标签
|
||
|
||
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;
|
||
|
||
// 类别设置事件处理器
|
||
btnSetCategory.Click += BtnSetCategory_Click;
|
||
|
||
// 3D交互模式事件处理器
|
||
btnEnterPathEditMode.Click += BtnEnterPathEditMode_Click;
|
||
btnExitPathEditMode.Click += BtnExitPathEditMode_Click;
|
||
btnHighlightChannels.Click += BtnHighlightChannels_Click;
|
||
btnClearHighlight.Click += BtnClearHighlight_Click;
|
||
btnOpen2DMap.Click += BtnOpen2DMap_Click;
|
||
cmbPointType.SelectedIndexChanged += CmbPointType_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}");
|
||
|
||
// 添加后立即验证
|
||
WriteLog($"[GenerateChannelOutlines] 添加后验证 - 对象地址: {geometry.GetHashCode()}");
|
||
WriteLog($"[GenerateChannelOutlines] 添加后验证 - 边界矩形: ({geometry.BoundingRect.X},{geometry.BoundingRect.Y},{geometry.BoundingRect.Width},{geometry.BoundingRect.Height})");
|
||
|
||
// 验证列表中的对象
|
||
var lastAdded = _channelGeometries[_channelGeometries.Count - 1];
|
||
WriteLog($"[GenerateChannelOutlines] 列表中最后一个对象地址: {lastAdded.GetHashCode()}");
|
||
WriteLog($"[GenerateChannelOutlines] 列表中最后一个对象边界矩形: ({lastAdded.BoundingRect.X},{lastAdded.BoundingRect.Y},{lastAdded.BoundingRect.Width},{lastAdded.BoundingRect.Height})");
|
||
}
|
||
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 conversionFactor = GetUnitsToMetersConversionFactor();
|
||
|
||
// 转换包围盒到米单位
|
||
var boundingBoxInMeters = ConvertBoundingBoxToMeters(boundingBox, conversionFactor);
|
||
WriteLog($"转换后包围盒(米): Min({boundingBoxInMeters.Min.X:F2},{boundingBoxInMeters.Min.Y:F2},{boundingBoxInMeters.Min.Z:F2}) Max({boundingBoxInMeters.Max.X:F2},{boundingBoxInMeters.Max.Y:F2},{boundingBoxInMeters.Max.Z:F2})");
|
||
|
||
var geometry = new ChannelGeometry
|
||
{
|
||
ChannelItem = channel,
|
||
Name = channel.DisplayName ?? "未命名通道"
|
||
};
|
||
|
||
// 从模型项提取真实几何顶点,包围盒已转换为米单位
|
||
var vertices = ExtractChannelVertices(channel, boundingBoxInMeters);
|
||
geometry.WorldVertices.AddRange(vertices);
|
||
WriteLog($"提取到 {vertices.Count} 个世界顶点");
|
||
|
||
// 转换为地图坐标
|
||
WriteLog($"开始转换 {vertices.Count} 个世界坐标到地图坐标");
|
||
WriteLog($"坐标转换器信息: {_coordinateConverter}");
|
||
|
||
foreach (var worldVertex in vertices)
|
||
{
|
||
WriteLog($"转换前: 世界坐标 ({worldVertex.X:F2},{worldVertex.Y:F2},{worldVertex.Z:F2})");
|
||
|
||
var mapPoint = _coordinateConverter.WorldToMap(worldVertex);
|
||
|
||
WriteLog($"转换后: 地图坐标 ({mapPoint.X:F2},{mapPoint.Y:F2})");
|
||
WriteLog($"地图坐标有效性检查: {_coordinateConverter.IsValidMapPoint(mapPoint)}");
|
||
|
||
geometry.MapVertices.Add(new PointF((float)mapPoint.X, (float)mapPoint.Y));
|
||
}
|
||
|
||
// 计算包围矩形
|
||
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);
|
||
|
||
WriteLog($"边界计算 - minX:{minX:F2}, maxX:{maxX:F2}, minY:{minY:F2}, maxY:{maxY:F2}");
|
||
|
||
var rectX = (int)Math.Floor(minX);
|
||
var rectY = (int)Math.Floor(minY);
|
||
var rectWidth = Math.Max(1, (int)Math.Ceiling(maxX - minX));
|
||
var rectHeight = Math.Max(1, (int)Math.Ceiling(maxY - minY));
|
||
|
||
WriteLog($"矩形参数 - x:{rectX}, y:{rectY}, width:{rectWidth}, height:{rectHeight}");
|
||
|
||
// 创建边界矩形并立即验证
|
||
var boundingRect = new Rectangle(rectX, rectY, rectWidth, rectHeight);
|
||
WriteLog($"创建的矩形对象: X={boundingRect.X}, Y={boundingRect.Y}, Width={boundingRect.Width}, Height={boundingRect.Height}");
|
||
|
||
// 赋值给几何对象
|
||
geometry.BoundingRect = boundingRect;
|
||
|
||
WriteLog($"赋值后几何对象的边界矩形: ({geometry.BoundingRect.X},{geometry.BoundingRect.Y},{geometry.BoundingRect.Width},{geometry.BoundingRect.Height})");
|
||
|
||
// 再次验证
|
||
if (geometry.BoundingRect.Width <= 0 || geometry.BoundingRect.Height <= 0)
|
||
{
|
||
WriteLog($"警告:边界矩形无效,使用默认矩形");
|
||
geometry.BoundingRect = new Rectangle(100, 100, 100, 100);
|
||
WriteLog($"设置默认矩形后: ({geometry.BoundingRect.X},{geometry.BoundingRect.Y},{geometry.BoundingRect.Width},{geometry.BoundingRect.Height})");
|
||
}
|
||
|
||
// 最终验证
|
||
WriteLog($"最终边界矩形验证: ({geometry.BoundingRect.X},{geometry.BoundingRect.Y},{geometry.BoundingRect.Width},{geometry.BoundingRect.Height})");
|
||
}
|
||
else
|
||
{
|
||
WriteLog("警告:没有有效的地图顶点,使用默认边界矩形");
|
||
geometry.BoundingRect = new Rectangle(100, 100, 100, 100);
|
||
}
|
||
|
||
// 强制启用详细几何显示和坐标标注
|
||
geometry.ShowDetailedGeometry = true;
|
||
geometry.ShowVertexLabels = true;
|
||
|
||
WriteLog($"设置显示标志 - ShowDetailedGeometry: {geometry.ShowDetailedGeometry}, ShowVertexLabels: {geometry.ShowVertexLabels}");
|
||
|
||
// 最终验证返回的对象
|
||
WriteLog($"[CreateChannelGeometry] 返回前最终检查:");
|
||
WriteLog($"[CreateChannelGeometry] 对象地址: {geometry.GetHashCode()}");
|
||
WriteLog($"[CreateChannelGeometry] 边界矩形: ({geometry.BoundingRect.X},{geometry.BoundingRect.Y},{geometry.BoundingRect.Width},{geometry.BoundingRect.Height})");
|
||
WriteLog($"[CreateChannelGeometry] 顶点数量: World={geometry.WorldVertices.Count}, Map={geometry.MapVertices.Count}");
|
||
|
||
WriteLog($"=== 通道几何创建完成 ===");
|
||
return geometry;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"创建通道几何信息失败: {ex.Message}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 提取通道顶点
|
||
/// </summary>
|
||
/// <param name="boundingBox">包围盒</param>
|
||
/// <returns>顶点集合</returns>
|
||
/// <summary>
|
||
/// 从模型项提取真实的通道顶视图顶点
|
||
/// </summary>
|
||
/// <param name="channel">通道模型项</param>
|
||
/// <param name="boundingBox">包围盒(用于回退处理)</param>
|
||
/// <returns>通道顶点列表</returns>
|
||
private List<Point3D> ExtractChannelVertices(ModelItem channel, BoundingBox3D boundingBox)
|
||
{
|
||
var vertices = new List<Point3D>();
|
||
|
||
try
|
||
{
|
||
WriteLog($"=== 开始提取通道真实几何: {channel.DisplayName} ===");
|
||
WriteLog($"通道类型: {channel.GetType().Name}");
|
||
WriteLog($"通道GUID: {channel.InstanceGuid}");
|
||
WriteLog($"通道包含几何: {channel.HasGeometry}");
|
||
WriteLog($"通道子项数量: {channel.Children.Count()}");
|
||
|
||
// 获取单位转换系数
|
||
var conversionFactor = GetUnitsToMetersConversionFactor();
|
||
WriteLog($"单位转换系数: {conversionFactor}");
|
||
|
||
// 首先尝试使用几何提取器获取真实轮廓
|
||
WriteLog("调用 GeometryExtractor.ExtractTopViewOutline...");
|
||
var realOutline = GeometryExtractor.ExtractTopViewOutline(channel, 0.5);
|
||
WriteLog($"GeometryExtractor 返回结果: {realOutline?.Count ?? -1} 个点");
|
||
|
||
if (realOutline.Count >= 3)
|
||
{
|
||
WriteLog($"成功提取真实轮廓,顶点数: {realOutline.Count}");
|
||
|
||
// 转换单位到米
|
||
foreach (var point in realOutline)
|
||
{
|
||
var convertedPoint = ConvertToMeters(point, conversionFactor);
|
||
vertices.Add(convertedPoint);
|
||
WriteLog($" 真实顶点: ({convertedPoint.X:F3}, {convertedPoint.Y:F3}, {convertedPoint.Z:F3})");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
WriteLog($"无法提取真实轮廓(点数: {realOutline.Count}),使用包围盒回退方案");
|
||
|
||
// 回退到包围盒方案
|
||
vertices = ExtractChannelVerticesFromBoundingBox(boundingBox, conversionFactor);
|
||
}
|
||
|
||
WriteLog($"最终生成了 {vertices.Count} 个通道顶点");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"提取通道顶点失败: {ex.Message},使用包围盒回退方案");
|
||
|
||
try
|
||
{
|
||
var conversionFactor = GetUnitsToMetersConversionFactor();
|
||
vertices = ExtractChannelVerticesFromBoundingBox(boundingBox, conversionFactor);
|
||
}
|
||
catch (Exception ex2)
|
||
{
|
||
WriteLog($"包围盒回退方案也失败: {ex2.Message}");
|
||
}
|
||
}
|
||
|
||
return vertices;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从包围盒提取通道顶点(回退方案)
|
||
/// </summary>
|
||
/// <param name="boundingBox">包围盒</param>
|
||
/// <param name="conversionFactor">单位转换系数</param>
|
||
/// <returns>通道顶点列表</returns>
|
||
private List<Point3D> ExtractChannelVerticesFromBoundingBox(BoundingBox3D boundingBox, double conversionFactor)
|
||
{
|
||
var vertices = new List<Point3D>();
|
||
|
||
try
|
||
{
|
||
WriteLog("使用包围盒生成通道顶点");
|
||
|
||
// 转换包围盒到米单位
|
||
var convertedBoundingBox = ConvertBoundingBoxToMeters(boundingBox, conversionFactor);
|
||
WriteLog($"转换后包围盒: Min({convertedBoundingBox.Min.X:F2},{convertedBoundingBox.Min.Y:F2},{convertedBoundingBox.Min.Z:F2}) Max({convertedBoundingBox.Max.X:F2},{convertedBoundingBox.Max.Y:F2},{convertedBoundingBox.Max.Z:F2})");
|
||
|
||
// 计算尺寸
|
||
double width = convertedBoundingBox.Max.X - convertedBoundingBox.Min.X;
|
||
double length = convertedBoundingBox.Max.Y - convertedBoundingBox.Min.Y;
|
||
double height = convertedBoundingBox.Max.Z - convertedBoundingBox.Min.Z;
|
||
|
||
WriteLog($"通道尺寸 - 宽度:{width:F2}m, 长度:{length:F2}m, 高度:{height:F2}m");
|
||
|
||
// 检查尺寸是否合理
|
||
double maxDimension = Math.Max(width, Math.Max(length, height));
|
||
if (maxDimension > 5000) // 提高阈值到5公里,更宽松的限制
|
||
{
|
||
WriteLog($"警告:通道尺寸过大({maxDimension:F2}m),将适度缩小");
|
||
|
||
// 计算中心点
|
||
var centerX = (convertedBoundingBox.Min.X + convertedBoundingBox.Max.X) / 2;
|
||
var centerY = (convertedBoundingBox.Min.Y + convertedBoundingBox.Max.Y) / 2;
|
||
var centerZ = (convertedBoundingBox.Min.Z + convertedBoundingBox.Max.Z) / 2;
|
||
|
||
// 适度缩小,保持长宽比
|
||
double scaleFactor = 1000.0 / maxDimension; // 缩放到最大1000米
|
||
double newWidth = width * scaleFactor;
|
||
double newLength = length * scaleFactor;
|
||
|
||
WriteLog($"缩放因子: {scaleFactor:F3}, 新尺寸: {newWidth:F2}m x {newLength:F2}m");
|
||
|
||
convertedBoundingBox = new BoundingBox3D(
|
||
new Point3D(centerX - newWidth/2, centerY - newLength/2, convertedBoundingBox.Min.Z),
|
||
new Point3D(centerX + newWidth/2, centerY + newLength/2, convertedBoundingBox.Max.Z)
|
||
);
|
||
|
||
WriteLog($"调整后包围盒: Min({convertedBoundingBox.Min.X:F2},{convertedBoundingBox.Min.Y:F2},{convertedBoundingBox.Min.Z:F2}) Max({convertedBoundingBox.Max.X:F2},{convertedBoundingBox.Max.Y:F2},{convertedBoundingBox.Max.Z:F2})");
|
||
}
|
||
else if (maxDimension < 1.0) // 如果太小,设置最小尺寸
|
||
{
|
||
WriteLog($"警告:通道尺寸过小({maxDimension:F2}m),将放大到可见尺寸");
|
||
|
||
var centerX = (convertedBoundingBox.Min.X + convertedBoundingBox.Max.X) / 2;
|
||
var centerY = (convertedBoundingBox.Min.Y + convertedBoundingBox.Max.Y) / 2;
|
||
|
||
// 设置最小可见尺寸(10米)
|
||
double minSize = 10.0;
|
||
WriteLog($"使用最小尺寸: {minSize:F2}m");
|
||
|
||
convertedBoundingBox = new BoundingBox3D(
|
||
new Point3D(centerX - minSize/2, centerY - minSize/2, convertedBoundingBox.Min.Z),
|
||
new Point3D(centerX + minSize/2, centerY + minSize/2, convertedBoundingBox.Max.Z)
|
||
);
|
||
}
|
||
|
||
// 生成矩形顶点(顶视图)
|
||
double z = convertedBoundingBox.Max.Z; // 使用顶部高度
|
||
|
||
vertices.Add(new Point3D(convertedBoundingBox.Min.X, convertedBoundingBox.Min.Y, z)); // 左下
|
||
vertices.Add(new Point3D(convertedBoundingBox.Max.X, convertedBoundingBox.Min.Y, z)); // 右下
|
||
vertices.Add(new Point3D(convertedBoundingBox.Max.X, convertedBoundingBox.Max.Y, z)); // 右上
|
||
vertices.Add(new Point3D(convertedBoundingBox.Min.X, convertedBoundingBox.Max.Y, z)); // 左上
|
||
|
||
WriteLog($"生成了 {vertices.Count} 个矩形顶点");
|
||
foreach (var vertex in vertices)
|
||
{
|
||
WriteLog($" 矩形顶点: ({vertex.X:F2}, {vertex.Y:F2}, {vertex.Z:F2})");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"包围盒顶点提取失败: {ex.Message}");
|
||
}
|
||
|
||
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()
|
||
{
|
||
WriteLog($"=== 开始重绘地图 ===");
|
||
WriteLog($"地图缓冲区状态: {(_mapBuffer != null ? "存在" : "不存在")}");
|
||
WriteLog($"地图图形状态: {(_mapGraphics != null ? "存在" : "不存在")}");
|
||
|
||
if (_mapGraphics == null)
|
||
{
|
||
WriteLog("警告:地图图形对象为空,尝试重新初始化");
|
||
if (_mapPanel.Width > 0 && _mapPanel.Height > 0)
|
||
{
|
||
InitializeMapBuffer();
|
||
}
|
||
else
|
||
{
|
||
WriteLog("地图面板尺寸无效,跳过重绘");
|
||
return;
|
||
}
|
||
}
|
||
|
||
try
|
||
{
|
||
WriteLog($"清空背景,地图尺寸: {_mapPanel.Width} x {_mapPanel.Height}");
|
||
|
||
// 清空背景 - 使用白色背景
|
||
_mapGraphics.Clear(System.Drawing.Color.White);
|
||
|
||
// 绘制测试矩形以验证绘制功能
|
||
DrawTestRectangle();
|
||
|
||
WriteLog($"准备绘制通道轮廓,通道几何数量: {_channelGeometries.Count}");
|
||
WriteLog($"通道轮廓数量: {_channelOutlines.Count}");
|
||
|
||
// 绘制通道轮廓
|
||
DrawChannelOutlines();
|
||
|
||
WriteLog($"准备绘制路径,路径点数量: {_currentRoute?.Points?.Count ?? 0}");
|
||
|
||
// 绘制路径线
|
||
DrawPathLines();
|
||
|
||
// 绘制路径点
|
||
DrawPathPoints();
|
||
|
||
WriteLog("地图重绘完成,刷新显示");
|
||
|
||
// 刷新显示
|
||
_mapPanel.Invalidate();
|
||
|
||
WriteLog($"=== 地图重绘结束 ===");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"重绘地图失败: {ex.Message}");
|
||
WriteLog($"堆栈跟踪: {ex.StackTrace}");
|
||
}
|
||
}
|
||
|
||
/// <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} ===");
|
||
WriteLog($"地图顶点数: {geometry.MapVertices.Count}");
|
||
WriteLog($"详细几何: {geometry.ShowDetailedGeometry}");
|
||
WriteLog($"边界矩形: ({geometry.BoundingRect.X},{geometry.BoundingRect.Y},{geometry.BoundingRect.Width},{geometry.BoundingRect.Height})");
|
||
WriteLog($"地图缓冲区尺寸: {(_mapBuffer?.Width ?? 0)} x {(_mapBuffer?.Height ?? 0)}");
|
||
|
||
if (_mapGraphics == null)
|
||
{
|
||
WriteLog("错误:地图图形对象为空,无法绘制");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
// 使用更明显的颜色来确保能看到
|
||
var fillColor = System.Drawing.Color.FromArgb(100, System.Drawing.Color.LightBlue); // 半透明蓝色
|
||
var borderColor = System.Drawing.Color.DarkBlue; // 深蓝色边框
|
||
var nameColor = System.Drawing.Color.Red; // 红色名称
|
||
|
||
using (var fillBrush = new SolidBrush(fillColor))
|
||
using (var borderPen = new Pen(borderColor, 3)) // 加粗边框
|
||
using (var vertexBrush = new SolidBrush(System.Drawing.Color.Red))
|
||
using (var labelFont = new Font("微软雅黑", 8))
|
||
using (var labelBrush = new SolidBrush(System.Drawing.Color.DarkBlue))
|
||
using (var nameFont = new Font("微软雅黑", 12, FontStyle.Bold))
|
||
using (var nameBrush = new SolidBrush(nameColor))
|
||
{
|
||
bool drawn = false;
|
||
|
||
// 方案1:尝试绘制多边形(如果有足够的顶点)
|
||
if (geometry.ShowDetailedGeometry && geometry.MapVertices.Count >= 3)
|
||
{
|
||
WriteLog($"绘制方案1:多边形,顶点数: {geometry.MapVertices.Count}");
|
||
|
||
foreach (var vertex in geometry.MapVertices)
|
||
{
|
||
WriteLog($" 顶点: ({vertex.X:F2},{vertex.Y:F2})");
|
||
}
|
||
|
||
_mapGraphics.FillPolygon(fillBrush, geometry.MapVertices.ToArray());
|
||
_mapGraphics.DrawPolygon(borderPen, geometry.MapVertices.ToArray());
|
||
drawn = true;
|
||
WriteLog("多边形绘制完成");
|
||
}
|
||
|
||
// 方案2:绘制边界矩形(作为主要显示方式)
|
||
if (geometry.BoundingRect.Width > 0 && geometry.BoundingRect.Height > 0)
|
||
{
|
||
WriteLog($"绘制方案2:边界矩形 ({geometry.BoundingRect.X},{geometry.BoundingRect.Y},{geometry.BoundingRect.Width},{geometry.BoundingRect.Height})");
|
||
|
||
// 检查边界矩形是否过大(可能覆盖整个地图)
|
||
bool isFullMapSize = (geometry.BoundingRect.Width >= _mapPanel.Width * 0.9 &&
|
||
geometry.BoundingRect.Height >= _mapPanel.Height * 0.9);
|
||
|
||
if (isFullMapSize)
|
||
{
|
||
WriteLog("警告:边界矩形几乎覆盖整个地图,使用中心区域绘制");
|
||
|
||
// 在地图中心绘制一个较小的矩形来表示通道
|
||
int centerX = _mapPanel.Width / 2;
|
||
int centerY = _mapPanel.Height / 2;
|
||
int rectSize = Math.Min(_mapPanel.Width, _mapPanel.Height) / 4;
|
||
|
||
var centerRect = new Rectangle(
|
||
centerX - rectSize / 2,
|
||
centerY - rectSize / 2,
|
||
rectSize,
|
||
rectSize
|
||
);
|
||
|
||
_mapGraphics.FillRectangle(fillBrush, centerRect);
|
||
_mapGraphics.DrawRectangle(borderPen, centerRect);
|
||
WriteLog($"中心矩形绘制: ({centerRect.X},{centerRect.Y},{centerRect.Width},{centerRect.Height})");
|
||
}
|
||
else
|
||
{
|
||
_mapGraphics.FillRectangle(fillBrush, geometry.BoundingRect);
|
||
_mapGraphics.DrawRectangle(borderPen, geometry.BoundingRect);
|
||
WriteLog("边界矩形绘制完成");
|
||
}
|
||
drawn = true;
|
||
}
|
||
|
||
// 方案3:回退方案 - 基于顶点计算矩形
|
||
if (!drawn && geometry.MapVertices.Count >= 2)
|
||
{
|
||
WriteLog($"绘制方案3:基于顶点计算矩形,顶点数: {geometry.MapVertices.Count}");
|
||
|
||
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));
|
||
WriteLog($"计算的矩形: ({rect.X},{rect.Y},{rect.Width},{rect.Height})");
|
||
|
||
if (rect.Width > 0 && rect.Height > 0)
|
||
{
|
||
_mapGraphics.FillRectangle(fillBrush, rect);
|
||
_mapGraphics.DrawRectangle(borderPen, rect);
|
||
drawn = true;
|
||
WriteLog("基于顶点的矩形绘制完成");
|
||
}
|
||
}
|
||
|
||
// 方案4:最后的回退方案 - 在地图中心绘制一个固定大小的矩形
|
||
if (!drawn)
|
||
{
|
||
WriteLog("绘制方案4:默认中心矩形");
|
||
|
||
int centerX = _mapPanel.Width / 2;
|
||
int centerY = _mapPanel.Height / 2;
|
||
var defaultRect = new Rectangle(centerX - 50, centerY - 50, 100, 100);
|
||
|
||
_mapGraphics.FillRectangle(fillBrush, defaultRect);
|
||
_mapGraphics.DrawRectangle(borderPen, defaultRect);
|
||
drawn = true;
|
||
WriteLog($"默认矩形绘制: ({defaultRect.X},{defaultRect.Y},{defaultRect.Width},{defaultRect.Height})");
|
||
}
|
||
|
||
// 绘制顶点标记(如果启用)
|
||
if (geometry.ShowVertexLabels && geometry.ShowDetailedGeometry && geometry.MapVertices.Count > 0)
|
||
{
|
||
WriteLog($"绘制顶点标记,数量: {geometry.MapVertices.Count}");
|
||
|
||
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 - 4, mapVertex.Y - 4, 8, 8);
|
||
_mapGraphics.FillEllipse(vertexBrush, vertexRect);
|
||
|
||
// 绘制坐标标签
|
||
var label = $"({worldVertex.X:F1},{worldVertex.Y:F1})";
|
||
var labelSize = _mapGraphics.MeasureString(label, labelFont);
|
||
|
||
var labelX = mapVertex.X + 10;
|
||
var labelY = mapVertex.Y - labelSize.Height / 2;
|
||
|
||
// 确保标签在地图范围内
|
||
if (labelX + labelSize.Width > _mapPanel.Width)
|
||
labelX = mapVertex.X - labelSize.Width - 10;
|
||
if (labelY < 0) labelY = mapVertex.Y + 10;
|
||
if (labelY + labelSize.Height > _mapPanel.Height)
|
||
labelY = mapVertex.Y - labelSize.Height - 10;
|
||
|
||
// 绘制标签背景
|
||
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))
|
||
{
|
||
WriteLog($"绘制通道名称: {geometry.Name}");
|
||
|
||
float centerX, centerY;
|
||
|
||
if (geometry.MapVertices.Count > 0)
|
||
{
|
||
centerX = geometry.MapVertices.Average(p => p.X);
|
||
centerY = geometry.MapVertices.Average(p => p.Y);
|
||
}
|
||
else
|
||
{
|
||
centerX = _mapPanel.Width / 2f;
|
||
centerY = _mapPanel.Height / 2f;
|
||
}
|
||
|
||
var nameSize = _mapGraphics.MeasureString(geometry.Name, nameFont);
|
||
var nameX = centerX - nameSize.Width / 2;
|
||
var nameY = centerY - nameSize.Height / 2;
|
||
|
||
// 绘制名称背景
|
||
var nameRect = new RectangleF(nameX - 4, nameY - 2, nameSize.Width + 8, nameSize.Height + 4);
|
||
_mapGraphics.FillRectangle(new SolidBrush(System.Drawing.Color.FromArgb(200, System.Drawing.Color.Yellow)), nameRect);
|
||
_mapGraphics.DrawRectangle(new Pen(System.Drawing.Color.Black, 2), Rectangle.Round(nameRect));
|
||
|
||
// 绘制名称文本
|
||
_mapGraphics.DrawString(geometry.Name, nameFont, nameBrush, nameX, nameY);
|
||
|
||
WriteLog($"通道名称绘制在: ({nameX:F1},{nameY:F1})");
|
||
}
|
||
}
|
||
|
||
WriteLog($"=== 通道几何绘制完成: {geometry.Name} ===");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"绘制通道几何失败: {geometry.Name}, 错误: {ex.Message}");
|
||
WriteLog($"堆栈跟踪: {ex.StackTrace}");
|
||
}
|
||
}
|
||
|
||
/// <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>
|
||
/// <summary>
|
||
/// 绘制测试矩形以验证绘制功能
|
||
/// </summary>
|
||
private void DrawTestRectangle()
|
||
{
|
||
if (_mapGraphics == null) return;
|
||
|
||
WriteLog($"=== 开始绘制测试矩形 ===");
|
||
|
||
try
|
||
{
|
||
// 绘制一个右下角的测试矩形
|
||
var testRect = new Rectangle(
|
||
_mapBuffer.Width - 120, // 右下角X(距离右边20像素)
|
||
_mapBuffer.Height - 100, // 右下角Y(距离底部20像素)
|
||
100, 80
|
||
);
|
||
|
||
using (var testBrush = new SolidBrush(System.Drawing.Color.FromArgb(100, System.Drawing.Color.Green)))
|
||
using (var testPen = new Pen(System.Drawing.Color.DarkGreen, 2))
|
||
{
|
||
_mapGraphics.FillRectangle(testBrush, testRect);
|
||
_mapGraphics.DrawRectangle(testPen, testRect);
|
||
}
|
||
|
||
WriteLog($"测试矩形绘制完成: ({testRect.X},{testRect.Y},{testRect.Width},{testRect.Height})");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"测试矩形绘制失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
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}");
|
||
|
||
// 详细调试边界矩形
|
||
var rect = firstGeometry.BoundingRect;
|
||
WriteLog($"[DrawDebugInfo] 读取到的边界矩形: X={rect.X}, Y={rect.Y}, Width={rect.Width}, Height={rect.Height}");
|
||
|
||
info.Add($"边界矩形: ({rect.X},{rect.Y},{rect.Width},{rect.Height})");
|
||
info.Add($"详细几何: {firstGeometry.ShowDetailedGeometry}");
|
||
info.Add($"显示标注: {firstGeometry.ShowVertexLabels}");
|
||
|
||
// 检查引用是否有问题
|
||
WriteLog($"[DrawDebugInfo] ChannelGeometry对象地址: {firstGeometry.GetHashCode()}");
|
||
WriteLog($"[DrawDebugInfo] BoundingRect对象信息: IsEmpty={rect.IsEmpty}, Location={rect.Location}, Size={rect.Size}");
|
||
|
||
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})");
|
||
}
|
||
|
||
// 再次验证数据完整性
|
||
info.Add($"对象引用: {(firstGeometry != null ? "有效" : "无效")}");
|
||
info.Add($"列表索引0: {(_channelGeometries[0] != null ? "有效" : "无效")}");
|
||
}
|
||
|
||
// 路径信息
|
||
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
|
||
{
|
||
WriteLog($"=== 用户点击重算边界按钮 ===");
|
||
|
||
// 显示进度信息
|
||
statusLabel.Text = "正在重新计算边界...";
|
||
|
||
// 清除现有的几何数据
|
||
_channelOutlines.Clear();
|
||
_channelGeometries.Clear();
|
||
WriteLog("已清除现有几何数据");
|
||
|
||
// 重新初始化地图缓冲区
|
||
InitializeMapBuffer();
|
||
WriteLog("已重新初始化地图缓冲区");
|
||
|
||
// 重新生成通道轮廓
|
||
GenerateChannelOutlines();
|
||
WriteLog($"重新生成了 {_channelGeometries.Count} 个通道几何");
|
||
|
||
// 强制重绘地图
|
||
ForceRedraw();
|
||
WriteLog("强制重绘完成");
|
||
|
||
// 更新状态
|
||
statusLabel.Text = $"边界重算完成 - 通道数量: {_channelGeometries.Count}";
|
||
|
||
// 显示结果消息
|
||
var message = $"边界重算完成!\n\n" +
|
||
$"通道数量: {_channelItems.Count}\n" +
|
||
$"几何对象: {_channelGeometries.Count}\n" +
|
||
$"轮廓矩形: {_channelOutlines.Count}\n" +
|
||
$"地图尺寸: {_mapPanel.Width} x {_mapPanel.Height}";
|
||
|
||
if (_channelGeometries.Count > 0)
|
||
{
|
||
var firstGeometry = _channelGeometries[0];
|
||
message += $"\n\n第一个通道: {firstGeometry.Name}\n";
|
||
message += $"边界矩形: ({firstGeometry.BoundingRect.X},{firstGeometry.BoundingRect.Y},{firstGeometry.BoundingRect.Width},{firstGeometry.BoundingRect.Height})\n";
|
||
message += $"地图顶点数: {firstGeometry.MapVertices.Count}";
|
||
}
|
||
|
||
MessageBox.Show(message, "重算边界", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||
|
||
WriteLog($"=== 重算边界操作完成 ===");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"重算边界失败: {ex.Message}");
|
||
WriteLog($"堆栈跟踪: {ex.StackTrace}");
|
||
|
||
statusLabel.Text = "重算边界失败";
|
||
MessageBox.Show($"重算边界时发生错误:\n{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
#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
|
||
|
||
/// <summary>
|
||
/// 获取Navisworks文档单位并转换为米的系数
|
||
/// </summary>
|
||
/// <returns>转换系数(文档单位转换为米)</returns>
|
||
private double GetUnitsToMetersConversionFactor()
|
||
{
|
||
try
|
||
{
|
||
var units = Autodesk.Navisworks.Api.Application.ActiveDocument.Units;
|
||
WriteLog($"文档单位: {units}");
|
||
|
||
switch (units)
|
||
{
|
||
case Units.Millimeters:
|
||
WriteLog("检测到单位:毫米,转换系数:0.001");
|
||
return 0.001;
|
||
case Units.Centimeters:
|
||
WriteLog("检测到单位:厘米,转换系数:0.01");
|
||
return 0.01;
|
||
case Units.Meters:
|
||
WriteLog("检测到单位:米,转换系数:1.0");
|
||
return 1.0;
|
||
case Units.Inches:
|
||
WriteLog("检测到单位:英寸,转换系数:0.0254");
|
||
return 0.0254;
|
||
case Units.Feet:
|
||
WriteLog("检测到单位:英尺,转换系数:0.3048");
|
||
return 0.3048;
|
||
case Units.Kilometers:
|
||
WriteLog("检测到单位:公里,转换系数:1000.0");
|
||
return 1000.0;
|
||
case Units.Micrometers:
|
||
WriteLog("检测到单位:微米,转换系数:0.000001");
|
||
return 0.000001;
|
||
case Units.Microinches:
|
||
WriteLog("检测到单位:微英寸,转换系数:0.0000000254");
|
||
return 0.0000000254;
|
||
case Units.Mils:
|
||
WriteLog("检测到单位:密尔,转换系数:0.0000254");
|
||
return 0.0000254;
|
||
case Units.Yards:
|
||
WriteLog("检测到单位:码,转换系数:0.9144");
|
||
return 0.9144;
|
||
case Units.Miles:
|
||
WriteLog("检测到单位:英里,转换系数:1609.43");
|
||
return 1609.43;
|
||
default:
|
||
WriteLog("未知单位,默认为米,转换系数:1.0");
|
||
return 1.0;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"获取文档单位失败: {ex.Message},默认为米");
|
||
return 1.0;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转换坐标到米单位
|
||
/// </summary>
|
||
/// <param name="point">原始坐标点</param>
|
||
/// <param name="conversionFactor">转换系数</param>
|
||
/// <returns>转换后的坐标点(米单位)</returns>
|
||
private Point3D ConvertToMeters(Point3D point, double conversionFactor)
|
||
{
|
||
return new Point3D(
|
||
point.X * conversionFactor,
|
||
point.Y * conversionFactor,
|
||
point.Z * conversionFactor
|
||
);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 转换包围盒到米单位
|
||
/// </summary>
|
||
/// <param name="boundingBox">原始包围盒</param>
|
||
/// <param name="conversionFactor">转换系数</param>
|
||
/// <returns>转换后的包围盒(米单位)</returns>
|
||
private BoundingBox3D ConvertBoundingBoxToMeters(BoundingBox3D boundingBox, double conversionFactor)
|
||
{
|
||
var minPoint = ConvertToMeters(boundingBox.Min, conversionFactor);
|
||
var maxPoint = ConvertToMeters(boundingBox.Max, conversionFactor);
|
||
return new BoundingBox3D(minPoint, maxPoint);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据实际通道几何重新计算坐标转换器范围
|
||
/// </summary>
|
||
private void RecalculateCoordinateConverterBounds()
|
||
{
|
||
WriteLog($"=== 开始重新计算坐标转换器范围 ===");
|
||
|
||
if (_channelGeometries.Count == 0)
|
||
{
|
||
WriteLog("没有通道几何,跳过坐标转换器范围重计算");
|
||
return;
|
||
}
|
||
|
||
// 收集所有实际的世界顶点
|
||
var allWorldVertices = new List<Point3D>();
|
||
foreach (var geometry in _channelGeometries)
|
||
{
|
||
allWorldVertices.AddRange(geometry.WorldVertices);
|
||
}
|
||
|
||
if (allWorldVertices.Count == 0)
|
||
{
|
||
WriteLog("没有世界顶点,跳过坐标转换器范围重计算");
|
||
return;
|
||
}
|
||
|
||
// 计算实际的世界坐标范围
|
||
var minX = allWorldVertices.Min(v => v.X);
|
||
var maxX = allWorldVertices.Max(v => v.X);
|
||
var minY = allWorldVertices.Min(v => v.Y);
|
||
var maxY = allWorldVertices.Max(v => v.Y);
|
||
var minZ = allWorldVertices.Min(v => v.Z);
|
||
var maxZ = allWorldVertices.Max(v => v.Z);
|
||
|
||
WriteLog($"实际世界坐标范围: X({minX:F3},{maxX:F3}) Y({minY:F3},{maxY:F3}) Z({minZ:F3},{maxZ:F3})");
|
||
|
||
// 创建新的通道边界
|
||
var actualBounds = new BoundingBox3D(
|
||
new Point3D(minX, minY, minZ),
|
||
new Point3D(maxX, maxY, maxZ)
|
||
);
|
||
var newChannelBounds = new ChannelBounds(actualBounds);
|
||
|
||
WriteLog($"更新前坐标转换器信息: {_coordinateConverter}");
|
||
|
||
// 更新坐标转换器的通道边界
|
||
_coordinateConverter.UpdateChannelBounds(newChannelBounds);
|
||
|
||
WriteLog($"更新后坐标转换器信息: {_coordinateConverter}");
|
||
|
||
// 重新转换所有地图坐标
|
||
foreach (var geometry in _channelGeometries)
|
||
{
|
||
geometry.MapVertices.Clear();
|
||
foreach (var worldVertex in geometry.WorldVertices)
|
||
{
|
||
var mapPoint = _coordinateConverter.WorldToMap(worldVertex);
|
||
geometry.MapVertices.Add(new PointF((float)mapPoint.X, (float)mapPoint.Y));
|
||
}
|
||
|
||
// 重新计算边界矩形
|
||
if (geometry.MapVertices.Count > 0)
|
||
{
|
||
var minMapX = geometry.MapVertices.Min(p => p.X);
|
||
var maxMapX = geometry.MapVertices.Max(p => p.X);
|
||
var minMapY = geometry.MapVertices.Min(p => p.Y);
|
||
var maxMapY = geometry.MapVertices.Max(p => p.Y);
|
||
|
||
var rectX = (int)Math.Floor(minMapX);
|
||
var rectY = (int)Math.Floor(minMapY);
|
||
var rectWidth = Math.Max(1, (int)Math.Ceiling(maxMapX - minMapX));
|
||
var rectHeight = Math.Max(1, (int)Math.Ceiling(maxMapY - minMapY));
|
||
|
||
geometry.BoundingRect = new Rectangle(rectX, rectY, rectWidth, rectHeight);
|
||
WriteLog($"重新计算几何边界矩形: {geometry.Name} -> ({rectX},{rectY},{rectWidth},{rectHeight})");
|
||
}
|
||
}
|
||
|
||
WriteLog($"=== 坐标转换器范围重计算完成 ===");
|
||
}
|
||
|
||
#region 类别设置事件处理器
|
||
|
||
/// <summary>
|
||
/// 应用类别按钮点击事件
|
||
/// </summary>
|
||
private void BtnSetCategory_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (cmbCategoryType.SelectedIndex >= 0)
|
||
{
|
||
var categoryName = cmbCategoryType.SelectedItem.ToString();
|
||
WriteLog($"准备将选中的模型设置为类别: {categoryName}");
|
||
|
||
// 获取当前选中的模型
|
||
var currentSelection = Autodesk.Navisworks.Api.Application.ActiveDocument.CurrentSelection.SelectedItems;
|
||
if (currentSelection.Count() == 0)
|
||
{
|
||
System.Windows.Forms.MessageBox.Show("请先在Navisworks中选择要设置类别的模型。", "提示",
|
||
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information);
|
||
return;
|
||
}
|
||
|
||
WriteLog($"找到 {currentSelection.Count()} 个选中的模型,正在应用类别...");
|
||
System.Windows.Forms.MessageBox.Show($"已将 {currentSelection.Count()} 个模型设置为类别: {categoryName}", "成功",
|
||
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"应用类别失败: {ex.Message}");
|
||
System.Windows.Forms.MessageBox.Show($"应用类别失败: {ex.Message}", "错误",
|
||
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 3D交互模式事件处理器
|
||
|
||
/// <summary>
|
||
/// 进入路径编辑模式按钮点击事件
|
||
/// </summary>
|
||
private void BtnEnterPathEditMode_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_pathPlanningManager != null && _pathPlanningManager.EnterPathEditMode())
|
||
{
|
||
lblCurrentMode.Text = "当前模式: 3D路径编辑模式";
|
||
lblCurrentMode.ForeColor = System.Drawing.Color.DarkGreen;
|
||
btnEnterPathEditMode.Enabled = false;
|
||
btnExitPathEditMode.Enabled = true;
|
||
_isListeningFor3DClicks = true;
|
||
WriteLog("已进入3D路径编辑模式");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"进入路径编辑模式失败: {ex.Message}");
|
||
System.Windows.Forms.MessageBox.Show($"进入路径编辑模式失败: {ex.Message}", "错误",
|
||
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 退出路径编辑模式按钮点击事件
|
||
/// </summary>
|
||
private void BtnExitPathEditMode_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_pathPlanningManager != null && _pathPlanningManager.ExitPathEditMode())
|
||
{
|
||
lblCurrentMode.Text = "当前模式: 2D地图模式";
|
||
lblCurrentMode.ForeColor = System.Drawing.Color.DarkBlue;
|
||
btnEnterPathEditMode.Enabled = true;
|
||
btnExitPathEditMode.Enabled = false;
|
||
_isListeningFor3DClicks = false;
|
||
WriteLog("已退出3D路径编辑模式");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"退出路径编辑模式失败: {ex.Message}");
|
||
System.Windows.Forms.MessageBox.Show($"退出路径编辑模式失败: {ex.Message}", "错误",
|
||
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 高亮通道按钮点击事件
|
||
/// </summary>
|
||
private void BtnHighlightChannels_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.HighlightSelectedChannels();
|
||
WriteLog("已高亮选中的通道");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"高亮通道失败: {ex.Message}");
|
||
System.Windows.Forms.MessageBox.Show($"高亮通道失败: {ex.Message}", "错误",
|
||
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除高亮按钮点击事件
|
||
/// </summary>
|
||
private void BtnClearHighlight_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.ClearChannelHighlighting();
|
||
WriteLog("已清除通道高亮");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"清除高亮失败: {ex.Message}");
|
||
System.Windows.Forms.MessageBox.Show($"清除高亮失败: {ex.Message}", "错误",
|
||
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 打开2D地图按钮点击事件
|
||
/// </summary>
|
||
private void BtnOpen2DMap_Click(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
// 切换到2D地图模式,显示完整的地图界面
|
||
if (_pathPlanningManager != null)
|
||
{
|
||
_pathPlanningManager.ShowNavigationMap();
|
||
WriteLog("已打开2D导航地图");
|
||
}
|
||
else
|
||
{
|
||
WriteLog("路径管理器未初始化,无法打开2D导航地图");
|
||
System.Windows.Forms.MessageBox.Show("路径管理器未初始化,无法打开2D导航地图。", "提示",
|
||
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"打开2D导航地图失败: {ex.Message}");
|
||
System.Windows.Forms.MessageBox.Show($"打开2D导航地图失败: {ex.Message}", "错误",
|
||
System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 点类型选择改变事件
|
||
/// </summary>
|
||
private void CmbPointType_SelectedIndexChanged(object sender, EventArgs e)
|
||
{
|
||
try
|
||
{
|
||
if (_pathPlanningManager != null && cmbPointType.SelectedIndex >= 0)
|
||
{
|
||
PathPointType newType;
|
||
switch (cmbPointType.SelectedIndex)
|
||
{
|
||
case 0: newType = PathPointType.StartPoint; break;
|
||
case 1: newType = PathPointType.EndPoint; break;
|
||
default: newType = PathPointType.WayPoint; break;
|
||
}
|
||
_pathPlanningManager.CurrentPointType = newType;
|
||
WriteLog($"已设置当前点类型为: {newType}");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"设置点类型失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 确保3D交互按钮可见(调试用)
|
||
/// </summary>
|
||
private void EnsureButtonsVisible()
|
||
{
|
||
try
|
||
{
|
||
if (btnEnterPathEditMode != null)
|
||
{
|
||
btnEnterPathEditMode.Visible = true;
|
||
WriteLog("设置btnEnterPathEditMode可见");
|
||
}
|
||
if (btnExitPathEditMode != null)
|
||
{
|
||
btnExitPathEditMode.Visible = true;
|
||
WriteLog("设置btnExitPathEditMode可见");
|
||
}
|
||
if (btnHighlightChannels != null)
|
||
{
|
||
btnHighlightChannels.Visible = true;
|
||
WriteLog("设置btnHighlightChannels可见");
|
||
}
|
||
if (btnClearHighlight != null)
|
||
{
|
||
btnClearHighlight.Visible = true;
|
||
WriteLog("设置btnClearHighlight可见");
|
||
}
|
||
if (btnOpen2DMap != null)
|
||
{
|
||
btnOpen2DMap.Visible = true;
|
||
WriteLog("设置btnOpen2DMap可见");
|
||
}
|
||
if (cmbPointType != null)
|
||
{
|
||
cmbPointType.Visible = true;
|
||
WriteLog("设置cmbPointType可见");
|
||
}
|
||
if (lblCurrentMode != null)
|
||
{
|
||
lblCurrentMode.Visible = true;
|
||
WriteLog("设置lblCurrentMode可见");
|
||
}
|
||
WriteLog("所有3D交互按钮可见性检查完成");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
WriteLog($"确保按钮可见性失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
} |