NavisworksTransport/NavigationMapWindow.cs

2552 lines
108 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}