NavisworksTransport/src/Core/PathPointRenderPlugin.cs

2207 lines
77 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.Linq;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Plugins;
using NavisworksTransport.Utils;
namespace NavisworksTransport
{
/// <summary>
/// 路径可视化模式枚举
/// </summary>
public enum PathVisualizationMode
{
/// <summary>
/// 标准连线模式 - 圆柱形连线
/// </summary>
StandardLine,
/// <summary>
/// 车辆通行空间模式 - 矩形通道
/// </summary>
VehicleSpace
}
/// <summary>
/// 网格点类型枚举
/// </summary>
public enum GridPointType
{
/// <summary>
/// 球形
/// </summary>
Sphere,
/// <summary>
/// 正方形
/// </summary>
Rectangle,
/// <summary>
/// 圆
/// </summary>
Circle
}
/// <summary>
/// 渲染颜色类型枚举
/// </summary>
public enum RenderColorType
{
/// <summary>
/// 路径起点颜色(绿色)
/// </summary>
StartPoint,
/// <summary>
/// 路径终点颜色(红色)
/// </summary>
EndPoint,
/// <summary>
/// 路径中间点颜色(洋红色)
/// </summary>
WayPoint,
/// <summary>
/// 连线颜色(橙色)
/// </summary>
Line,
/// <summary>
/// 车辆通行空间颜色(淡蓝色亚克力)
/// </summary>
VehicleSpace,
/// <summary>
/// 预览点颜色(白色)
/// </summary>
PreviewPoint,
/// <summary>
/// 预览连线颜色(灰色)
/// </summary>
PreviewLine,
/// <summary>
/// 未到达终点颜色(深红色)
/// </summary>
UnreachedEndPoint,
/// <summary>
/// 网格通道颜色(绿色)
/// </summary>
GridChannel,
/// <summary>
/// 网格障碍物颜色(灰色)
/// </summary>
GridObstacle,
/// <summary>
/// 网格未知区域颜色(红色)
/// </summary>
GridUnknown
}
/// <summary>
/// 渲染样式,包含颜色和透明度
/// </summary>
public struct RenderStyle
{
/// <summary>
/// 颜色
/// </summary>
public Color Color { get; }
/// <summary>
/// 透明度 (0.0 = 完全透明, 1.0 = 完全不透明)
/// </summary>
public double Alpha { get; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="color">颜色</param>
/// <param name="alpha">透明度</param>
public RenderStyle(Color color, double alpha)
{
Color = color;
Alpha = alpha;
}
public override string ToString()
{
return $"RenderStyle[Color={Color}, Alpha={Alpha:F2}]";
}
}
/// <summary>
/// 车辆通行空间标记,用于渲染车辆通道空间
/// </summary>
public class VehicleSpaceMarker
{
/// <summary>
/// 通道起点
/// </summary>
public Point3D StartPoint { get; set; }
/// <summary>
/// 通道终点
/// </summary>
public Point3D EndPoint { get; set; }
/// <summary>
/// 通道宽度(车辆长宽最大值+2倍安全间隙
/// </summary>
public double Width { get; set; }
/// <summary>
/// 通道高度(车辆高度+安全间隙)
/// </summary>
public double Height { get; set; }
/// <summary>
/// 通道颜色
/// </summary>
public Color Color { get; set; }
/// <summary>
/// 透明度0.0-1.0默认0.5即50%透明)
/// </summary>
public double Alpha { get; set; } = 0.5;
/// <summary>
/// 起点在路径中的索引
/// </summary>
public int FromIndex { get; set; }
/// <summary>
/// 终点在路径中的索引
/// </summary>
public int ToIndex { get; set; }
public override string ToString()
{
return $"VehicleSpaceMarker[{FromIndex}->{ToIndex}, 宽度={Width:F2}m, 高度={Height:F2}m, 透明度={Alpha:F1}]";
}
}
/// <summary>
/// 连线标记,用于渲染路径点之间的连接线
/// </summary>
public class LineMarker
{
/// <summary>
/// 连线起点
/// </summary>
public Point3D StartPoint { get; set; }
/// <summary>
/// 连线终点
/// </summary>
public Point3D EndPoint { get; set; }
/// <summary>
/// 连线颜色
/// </summary>
public Color Color { get; set; }
/// <summary>
/// 连线半径
/// </summary>
public double Radius { get; set; }
/// <summary>
/// 起点在路径中的索引
/// </summary>
public int FromIndex { get; set; }
/// <summary>
/// 终点在路径中的索引
/// </summary>
public int ToIndex { get; set; }
/// <summary>
/// 路径段类型
/// </summary>
public PathSegmentType SegmentType { get; set; } = PathSegmentType.Straight;
/// <summary>
/// 圆弧轨迹数据仅当SegmentType=Arc时有效
/// </summary>
public ArcTrajectory Trajectory { get; set; }
/// <summary>
/// 采样点列表(用于圆弧段的多段圆柱体渲染)
/// </summary>
public List<Point3D> SampledPoints { get; set; }
public override string ToString()
{
return $"LineMarker[{FromIndex}->{ToIndex}, 类型={SegmentType}, 起点=({StartPoint.X:F2},{StartPoint.Y:F2},{StartPoint.Z:F2}), 终点=({EndPoint.X:F2},{EndPoint.Y:F2},{EndPoint.Z:F2})]";
}
}
/// <summary>
/// 路径可视化数据,包含一个完整路径的所有可视化元素
/// </summary>
public class PathVisualization
{
/// <summary>
/// 路径唯一标识
/// </summary>
public string PathId { get; set; }
/// <summary>
/// 路径数据引用
/// </summary>
public PathRoute PathRoute { get; set; }
/// <summary>
/// 点标记集合
/// </summary>
public List<CircleMarker> PointMarkers { get; set; }
/// <summary>
/// 连线标记集合(已废弃,保留用于兼容)
/// </summary>
[Obsolete("使用ControlLineMarkers或PathLineMarkers代替")]
public List<LineMarker> LineMarkers { get; set; }
/// <summary>
/// 控制点连线集合(用户意图,半透明)
/// </summary>
public List<LineMarker> ControlLineMarkers { get; set; }
/// <summary>
/// 路径连线集合(真实路径,不透明)
/// </summary>
public List<LineMarker> PathLineMarkers { get; set; }
/// <summary>
/// 切点标记集合
/// </summary>
public List<SquareMarker> TangentMarkers { get; set; }
/// <summary>
/// 车辆通行空间标记集合
/// </summary>
public List<VehicleSpaceMarker> VehicleSpaceMarkers { get; set; }
/// <summary>
/// 是否显示控制点可视化(用户意图)
/// </summary>
public bool ShowControlVisualization { get; set; } = true;
/// <summary>
/// 最后更新时间
/// </summary>
public DateTime LastUpdated { get; set; }
/// <summary>
/// 构造函数
/// </summary>
public PathVisualization()
{
PointMarkers = new List<CircleMarker>();
LineMarkers = new List<LineMarker>();
ControlLineMarkers = new List<LineMarker>();
PathLineMarkers = new List<LineMarker>();
TangentMarkers = new List<SquareMarker>();
VehicleSpaceMarkers = new List<VehicleSpaceMarker>();
LastUpdated = DateTime.Now;
}
public override string ToString()
{
return $"PathVisualization[PathId={PathId}, 点数={PointMarkers.Count}, 控制连线={ControlLineMarkers.Count}, 路径连线={PathLineMarkers.Count}]";
}
}
/// <summary>
/// 路径点圆形标记渲染插件
/// 使用Graphics.Circle在3D空间绘制圆形标记
/// </summary>
[Plugin("PathPointRenderPlugin", "NavisworksTransport", DisplayName = "路径点圆形标记渲染器")]
public class PathPointRenderPlugin : RenderPlugin
{
private readonly object _lockObject = new object();
private Dictionary<string, PathVisualization> _pathVisualizations = new Dictionary<string, PathVisualization>();
private bool _isEnabled = true;
// 预览点标记
private CircleMarker _previewMarker = null;
// 预览连线标记
private List<LineMarker> _previewLines = new List<LineMarker>();
// 当前网格大小(米),用于自适应点大小计算
private double _currentGridSizeInMeters;
// 路径可视化模式配置
private PathVisualizationMode _visualizationMode = PathVisualizationMode.StandardLine;
// 网格点类型配置
private GridPointType _gridPointType = GridPointType.Rectangle;
// 车辆参数必须通过SetVehicleParameters方法设置
private double _vehicleLength;
private double _vehicleWidth;
private double _vehicleHeight;
private double _safetyMargin;
// 静态实例,用于外部访问
private static PathPointRenderPlugin _instance;
/// <summary>
/// 构造函数
/// </summary>
public PathPointRenderPlugin()
{
_instance = this;
}
/// <summary>
/// 获取静态实例
/// </summary>
public static PathPointRenderPlugin Instance => _instance;
/// <summary>
/// 路径可视化模式
/// </summary>
public PathVisualizationMode VisualizationMode
{
get { return _visualizationMode; }
set
{
_visualizationMode = value;
// 模式改变时刷新所有普通路径(排除网格)
RefreshNormalPaths();
}
}
/// <summary>
/// 网格点类型
/// </summary>
public GridPointType GridPointType
{
get { return _gridPointType; }
set
{
_gridPointType = value;
// 类型改变时刷新所有网格路径
RefreshGridPaths();
}
}
/// <summary>
/// 是否启用渲染
/// </summary>
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
// 触发视图刷新
if (Application.ActiveDocument?.ActiveView != null)
{
Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.Render);
}
}
}
/// <summary>
/// 当前路径总数
/// </summary>
public int PathCount
{
get
{
lock (_lockObject)
{
return _pathVisualizations.Count;
}
}
}
/// <summary>
/// 当前点标记总数
/// </summary>
public int MarkerCount
{
get
{
lock (_lockObject)
{
return _pathVisualizations.Values.Sum(v => v.PointMarkers.Count);
}
}
}
/// <summary>
/// Navisworks渲染回调方法
/// </summary>
/// <param name="view">当前视图</param>
/// <param name="graphics">图形上下文</param>
public override void Render(View view, Graphics graphics)
{
if (!_isEnabled) return;
try
{
// 快速检查避免频繁的API调用
var activeDoc = Application.ActiveDocument;
if (activeDoc?.Models == null || activeDoc.Models.Count == 0)
{
return; // 静默返回,避免日志泛滥
}
// 检查是否有路径或预览点需要渲染
int pathCount;
bool hasPreviewPoint;
lock (_lockObject)
{
pathCount = _pathVisualizations.Count;
hasPreviewPoint = _previewMarker != null;
}
if (pathCount == 0 && !hasPreviewPoint) return;
// 使用BeginModelContext确保正确的渲染上下文
graphics.BeginModelContext();
lock (_lockObject)
{
// 渲染所有正式路径
foreach (var visualization in _pathVisualizations.Values)
{
// 渲染控制点可视化(用户意图)
if (visualization.ShowControlVisualization)
{
// 渲染所有点标记
foreach (var pointMarker in visualization.PointMarkers)
{
graphics.Color(pointMarker.Color, pointMarker.Alpha);
RenderPointMarker(graphics, pointMarker);
}
// 渲染控制点连线(半透明)
foreach (var controlLineMarker in visualization.ControlLineMarkers)
{
graphics.Color(controlLineMarker.Color, 0.3); // 30%透明度
graphics.Cylinder(controlLineMarker.StartPoint, controlLineMarker.EndPoint, controlLineMarker.Radius);
}
}
// 渲染真实路径可视化Edges + 切点)
// 先渲染切点(在底层)
foreach (var tangentMarker in visualization.TangentMarkers)
{
graphics.Color(tangentMarker.Color, tangentMarker.Alpha);
RenderSquareMarker(graphics, tangentMarker);
}
// 再渲染路径连线(直线段和圆弧段)
foreach (var pathLineMarker in visualization.PathLineMarkers)
{
// 圆弧段使用半透明灰色,直线段使用正常颜色
if (pathLineMarker.SegmentType == PathSegmentType.Arc)
{
graphics.Color(GetRenderColor(RenderColorType.PreviewLine), 0.5); // 半透明灰色
}
else
{
graphics.Color(pathLineMarker.Color, 1.0); // 不透明
}
RenderLineMarker(graphics, pathLineMarker);
}
// 渲染车辆通行空间(矩形通道)
foreach (var vehicleSpaceMarker in visualization.VehicleSpaceMarkers)
{
graphics.Color(vehicleSpaceMarker.Color, vehicleSpaceMarker.Alpha);
RenderVehicleSpace(graphics, vehicleSpaceMarker);
}
}
// 渲染预览点(灰色)
if (_previewMarker != null && _previewMarker.IsVisible)
{
graphics.Color(_previewMarker.Color, 0.7); // 使用半透明效果
RenderPointMarker(graphics, _previewMarker);
}
// 渲染预览连线(灰色)
if (_previewLines.Count > 0)
{
foreach (var previewLine in _previewLines)
{
graphics.Color(previewLine.Color, 0.7); // 使用半透明效果
graphics.Cylinder(previewLine.StartPoint, previewLine.EndPoint, previewLine.Radius);
}
}
}
graphics.EndModelContext();
}
catch (Exception ex)
{
// 渲染异常应该静默处理避免影响Navisworks主程序
LogManager.Error($"[渲染异常] {ex.Message}", ex);
// 不输出堆栈信息,避免日志过量
}
}
#region
/// <summary>
/// 渲染完整路径
/// </summary>
/// <param name="pathRoute">路径数据</param>
public void RenderPath(PathRoute pathRoute)
{
if (pathRoute == null)
{
return;
}
try
{
var visualization = new PathVisualization
{
PathId = pathRoute.Id,
PathRoute = pathRoute
};
BuildVisualization(visualization);
lock (_lockObject)
{
_pathVisualizations[pathRoute.Id] = visualization;
}
RequestViewRefresh();
}
catch (Exception ex)
{
LogManager.Error($"[路径渲染] 渲染路径失败: {ex.Message}", ex);
}
}
/// <summary>
/// 只渲染点标记,不绘制连线(用于自动路径规划的起终点显示)
/// </summary>
public void RenderPointOnly(PathRoute pathRoute)
{
if (pathRoute == null)
{
return;
}
try
{
// 检查是否是网格可视化路径
bool isGridVisualization = pathRoute.Id == "grid_visualization_all" ||
pathRoute.Id == "grid_visualization_channel" ||
pathRoute.Id == "grid_visualization_door" ||
pathRoute.Id == "grid_visualization_unknown" ||
pathRoute.Id == "grid_visualization_obstacle";
var visualization = new PathVisualization
{
PathId = pathRoute.Id,
PathRoute = pathRoute
};
// 只构建点标记,不构建连线
BuildPointMarkersOnly(visualization);
lock (_lockObject)
{
_pathVisualizations[pathRoute.Id] = visualization;
}
RequestViewRefresh();
}
catch (Exception ex)
{
LogManager.Error($"[点标记渲染] 渲染点标记失败: {ex.Message}", ex);
}
}
/// <summary>
/// 只构建点标记,不构建连线
/// </summary>
private void BuildPointMarkersOnly(PathVisualization visualization)
{
// 清空现有标记
visualization.PointMarkers.Clear();
visualization.LineMarkers.Clear(); // 确保没有连线
var points = visualization.PathRoute.Points;
if (points.Count == 0) return;
// 检查是否是网格可视化路径
bool isGridVisualization = visualization.PathId == "grid_visualization_all" ||
visualization.PathId == "grid_visualization_channel" ||
visualization.PathId == "grid_visualization_door" ||
visualization.PathId == "grid_visualization_unknown" ||
visualization.PathId == "grid_visualization_obstacle";
// 按索引排序点
var sortedPoints = points.OrderBy(p => p.Index).ToList();
// 只构建点标记,不构建连线
foreach (var point in sortedPoints)
{
var pointMarker = CreatePointMarker(point);
visualization.PointMarkers.Add(pointMarker);
}
visualization.LastUpdated = DateTime.Now;
}
/// <summary>
/// 刷新路径可视化
/// </summary>
/// <param name="pathId">路径ID</param>
public void RefreshPath(string pathId)
{
lock (_lockObject)
{
if (_pathVisualizations.TryGetValue(pathId, out var visualization))
{
BuildVisualization(visualization);
RequestViewRefresh();
}
}
}
/// <summary>
/// 设置控制点可视化的显示状态
/// </summary>
/// <param name="pathId">路径ID</param>
/// <param name="show">是否显示</param>
public void SetControlVisualizationVisibility(string pathId, bool show)
{
lock (_lockObject)
{
if (_pathVisualizations.TryGetValue(pathId, out var visualization))
{
if (visualization.ShowControlVisualization != show)
{
visualization.ShowControlVisualization = show;
RequestViewRefresh();
}
}
}
}
/// <summary>
/// 切换控制点可视化的显示状态
/// </summary>
/// <param name="pathId">路径ID</param>
public void ToggleControlVisualization(string pathId)
{
lock (_lockObject)
{
if (_pathVisualizations.TryGetValue(pathId, out var visualization))
{
visualization.ShowControlVisualization = !visualization.ShowControlVisualization;
RequestViewRefresh();
}
}
}
/// <summary>
/// 判断是否为网格可视化路径
/// </summary>
private bool IsGridVisualizationPath(string pathId)
{
return pathId != null && (
pathId.StartsWith("grid_visualization_") ||
pathId == "grid_visualization_all" ||
pathId == "voxel_grid_visualization"); // 体素网格可视化
}
/// <summary>
/// 刷新所有普通路径(排除网格可视化)
/// </summary>
private void RefreshNormalPaths()
{
lock (_lockObject)
{
var normalPathIds = _pathVisualizations.Keys
.Where(id => !IsGridVisualizationPath(id))
.ToList();
foreach (var pathId in normalPathIds)
{
if (_pathVisualizations.TryGetValue(pathId, out var visualization))
{
BuildVisualization(visualization);
}
}
if (normalPathIds.Count > 0)
{
RequestViewRefresh();
}
}
}
/// <summary>
/// 刷新所有网格路径
/// </summary>
private void RefreshGridPaths()
{
lock (_lockObject)
{
var gridPathIds = _pathVisualizations.Keys
.Where(id => IsGridVisualizationPath(id))
.ToList();
foreach (var pathId in gridPathIds)
{
if (_pathVisualizations.TryGetValue(pathId, out var visualization))
{
BuildPointMarkersOnly(visualization);
}
}
if (gridPathIds.Count > 0)
{
RequestViewRefresh();
}
}
}
/// <summary>
/// 移除路径
/// </summary>
/// <param name="pathId">路径ID</param>
public void RemovePath(string pathId)
{
lock (_lockObject)
{
if (_pathVisualizations.Remove(pathId))
{
RequestViewRefresh();
}
}
}
/// <summary>
/// 清空所有路径
/// </summary>
public void ClearAllPaths()
{
lock (_lockObject)
{
var count = _pathVisualizations.Count;
_pathVisualizations.Clear();
RequestViewRefresh();
}
}
/// <summary>
/// 清空路径,但保留指定的路径
/// </summary>
/// <param name="excludedPathIds">要保留的路径ID列表</param>
public void ClearPathsExcept(params string[] excludedPathIds)
{
try
{
lock (_lockObject)
{
// 防御性编程:处理空参数
if (excludedPathIds == null)
{
excludedPathIds = new string[0];
}
var excludedSet = new HashSet<string>(excludedPathIds.Where(id => !string.IsNullOrEmpty(id)));
var toRemove = _pathVisualizations.Keys.Where(id => !excludedSet.Contains(id)).ToList();
// 检查排除的路径是否实际存在
var existingExcluded = excludedSet.Where(id => _pathVisualizations.ContainsKey(id)).ToList();
var nonExistingExcluded = excludedSet.Where(id => !_pathVisualizations.ContainsKey(id)).ToList();
int removedCount = 0;
foreach (var pathId in toRemove)
{
if (_pathVisualizations.Remove(pathId))
{
removedCount++;
}
}
if (removedCount > 0)
{
RequestViewRefresh();
}
}
}
catch (Exception ex)
{
LogManager.Error($"[选择性清空] 清理路径时发生异常: {ex.Message}", ex);
// 即使发生异常也不抛出,避免影响主流程
}
}
/// <summary>
/// 构建路径可视化
/// </summary>
/// <param name="visualization">路径可视化对象</param>
private void BuildVisualization(PathVisualization visualization)
{
// 清空现有标记
visualization.PointMarkers.Clear();
visualization.LineMarkers.Clear();
visualization.ControlLineMarkers.Clear();
visualization.PathLineMarkers.Clear();
visualization.TangentMarkers.Clear();
visualization.VehicleSpaceMarkers.Clear();
var points = visualization.PathRoute.Points;
if (points.Count == 0) return;
// 按索引排序点
var sortedPoints = points.OrderBy(p => p.Index).ToList();
// 构建点标记(所有控制点)
foreach (var point in sortedPoints)
{
var pointMarker = CreatePointMarker(point);
visualization.PointMarkers.Add(pointMarker);
}
// 构建控制点连线(用户意图,半透明)
BuildControlLines(visualization, sortedPoints);
// 构建路径连线真实路径不透明仅当Edges存在时
if (visualization.PathRoute.Edges != null && visualization.PathRoute.Edges.Count > 0)
{
BuildPathLines(visualization, sortedPoints);
}
else if (_visualizationMode == PathVisualizationMode.VehicleSpace)
{
// 构建车辆通行空间标记
for (int i = 0; i < sortedPoints.Count - 1; i++)
{
var currentPoint = sortedPoints[i];
var nextPoint = sortedPoints[i + 1];
var vehicleSpaceMarker = CreateVehicleSpaceMarker(currentPoint, nextPoint);
visualization.VehicleSpaceMarkers.Add(vehicleSpaceMarker);
}
}
// 如果路径未完成,添加灰色的原始终点标记
if (!visualization.PathRoute.IsComplete && visualization.PathRoute.OriginalEndPoint != null)
{
var originalEndPoint = visualization.PathRoute.OriginalEndPoint;
var unreachedStyle = GetRenderStyle(RenderColorType.UnreachedEndPoint);
var grayEndMarker = new CircleMarker
{
Center = originalEndPoint,
Normal = new Vector3D(0, 0, 1),
Radius = GetRadiusForPointType(PathPointType.EndPoint),
Color = unreachedStyle.Color, // 未到达终点颜色
Alpha = unreachedStyle.Alpha, // 透明度,统一管理
Filled = true,
PointType = PathPointType.EndPoint,
SequenceNumber = -1, // 特殊序号表示这是未到达的终点
CreatedTime = DateTime.Now
};
visualization.PointMarkers.Add(grayEndMarker);
}
visualization.LastUpdated = DateTime.Now;
}
/// <summary>
/// 构建控制点连线(用户意图,半透明)
/// </summary>
/// <param name="visualization">路径可视化对象</param>
/// <param name="sortedPoints">排序后的路径点列表</param>
private void BuildControlLines(PathVisualization visualization, List<PathPoint> sortedPoints)
{
// 构建控制点之间的连线(所有相邻控制点)
for (int i = 0; i < sortedPoints.Count - 1; i++)
{
var currentPoint = sortedPoints[i];
var nextPoint = sortedPoints[i + 1];
var controlLineMarker = new LineMarker
{
StartPoint = currentPoint.Position,
EndPoint = nextPoint.Position,
Color = GetRenderColor(RenderColorType.Line),
Radius = GetLineRadius() * 0.5, // 比实际路径细
SegmentType = PathSegmentType.Straight,
FromIndex = currentPoint.Index,
ToIndex = nextPoint.Index
};
visualization.ControlLineMarkers.Add(controlLineMarker);
}
}
/// <summary>
/// 构建路径连线(真实路径,不透明)
/// </summary>
/// <param name="visualization">路径可视化对象</param>
/// <param name="sortedPoints">排序后的路径点列表</param>
private void BuildPathLines(PathVisualization visualization, List<PathPoint> sortedPoints)
{
var edges = visualization.PathRoute.Edges;
// 1. 收集在路径上的控制点ID
var onPathPointIds = new HashSet<string>();
foreach (var edge in edges)
{
if (!string.IsNullOrEmpty(edge.StartPointId))
onPathPointIds.Add(edge.StartPointId);
if (!string.IsNullOrEmpty(edge.EndPointId))
onPathPointIds.Add(edge.EndPointId);
}
// 2. 更新点标记的透明度
foreach (var pointMarker in visualization.PointMarkers)
{
var point = sortedPoints.FirstOrDefault(p => p.Id == pointMarker.PathPoint?.Id);
if (point != null)
{
bool isOnPath = onPathPointIds.Contains(point.Id);
pointMarker.IsOffPath = !isOnPath;
}
}
// 3. 构建路径连线Edges包含直线段和圆弧段
foreach (var edge in edges)
{
if (edge.SegmentType == PathSegmentType.Straight)
{
// 直线段检查SampledPoints是否有效
if (edge.SampledPoints != null && edge.SampledPoints.Count >= 2)
{
var lineMarker = new LineMarker
{
StartPoint = edge.SampledPoints.First(),
EndPoint = edge.SampledPoints.Last(),
Color = GetRenderColor(RenderColorType.Line),
Radius = GetLineRadius(),
SegmentType = PathSegmentType.Straight,
SampledPoints = edge.SampledPoints
};
visualization.PathLineMarkers.Add(lineMarker);
}
}
else if (edge.SegmentType == PathSegmentType.Arc && edge.Trajectory != null)
{
// 圆弧段检查Trajectory是否有效
var arcMarker = new LineMarker
{
StartPoint = edge.Trajectory.Ts,
EndPoint = edge.Trajectory.Te,
Color = GetRenderColor(RenderColorType.Line),
Radius = GetLineRadius(),
SegmentType = PathSegmentType.Arc,
Trajectory = edge.Trajectory,
SampledPoints = edge.SampledPoints
};
visualization.PathLineMarkers.Add(arcMarker);
// 添加切点标记
AddTangentMarkers(visualization, edge);
}
}
}
/// <summary>
/// 添加切点标记
/// </summary>
/// <param name="visualization">路径可视化对象</param>
/// <param name="edge">路径边</param>
private void AddTangentMarkers(PathVisualization visualization, PathEdge edge)
{
if (edge.Trajectory == null) return;
// 切点颜色与路径点相同
var tangentColor = GetRenderColor(RenderColorType.WayPoint);
// 进入切点Ts
var tsMarker = new SquareMarker
{
Center = edge.Trajectory.Ts,
Normal = new Vector3D(0, 0, 1),
Size = GetLineRadius() * 1.5,
Color = tangentColor,
Alpha = 0.9,
EdgeId = edge.Id,
TangentType = TangentPointType.EntryTangent
};
visualization.TangentMarkers.Add(tsMarker);
// 退出切点Te
var teMarker = new SquareMarker
{
Center = edge.Trajectory.Te,
Normal = new Vector3D(0, 0, 1),
Size = GetLineRadius() * 1.5,
Color = tangentColor,
Alpha = 0.9,
EdgeId = edge.Id,
TangentType = TangentPointType.ExitTangent
};
visualization.TangentMarkers.Add(teMarker);
}
/// <summary>
/// 创建车辆通行空间标记
/// </summary>
/// <param name="fromPoint">起点</param>
/// <param name="toPoint">终点</param>
/// <returns>车辆通行空间标记</returns>
private VehicleSpaceMarker CreateVehicleSpaceMarker(PathPoint fromPoint, PathPoint toPoint)
{
// 获取单位转换系数
double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor();
// 计算车辆通行空间宽度(米)
double vehicleInflationWidthInMeters = Math.Max(_vehicleLength, _vehicleWidth) + 2 * _safetyMargin;
// 计算车辆通行空间高度(米):车辆高度 + 安全间隙
double vehicleSpaceHeightInMeters = _vehicleHeight + _safetyMargin;
var style = GetRenderStyle(RenderColorType.VehicleSpace);
return new VehicleSpaceMarker
{
StartPoint = fromPoint.Position,
EndPoint = toPoint.Position,
Width = vehicleInflationWidthInMeters * metersToModelUnits, // 转换为模型单位
Height = vehicleSpaceHeightInMeters * metersToModelUnits, // 转换为模型单位
Color = style.Color, // 车辆通行空间颜色
Alpha = style.Alpha, // 透明度,统一管理
FromIndex = fromPoint.Index,
ToIndex = toPoint.Index
};
}
/// <summary>
/// 创建点标记
/// </summary>
/// <param name="point">路径点</param>
/// <returns>圆形标记</returns>
private CircleMarker CreatePointMarker(PathPoint point)
{
// 检查是否是网格/体素可视化点(通过名称判断)
bool isGridVisualization = point.Name.StartsWith("网格(") || point.Name.StartsWith("网格_") ||
point.Name.StartsWith("体素("); // 体素点
// 确定网格点样式(颜色+透明度)
RenderStyle gridStyle = new RenderStyle(Color.White, 1.0); // 默认样式
if (isGridVisualization)
{
// 根据网格类型确定样式
if (point.Name.Contains("空洞") || point.Name.Contains("Unknown"))
{
gridStyle = GetRenderStyle(RenderColorType.GridUnknown);
}
else if (point.Name.Contains("障碍") || point.Name.Contains("Obstacle"))
{
gridStyle = GetRenderStyle(RenderColorType.GridObstacle);
}
else if (point.Name.Contains("通道") || point.Name.Contains("Channel") || point.Name.Contains("开放"))
{
gridStyle = GetRenderStyle(RenderColorType.GridChannel);
}
// 也可以通过Notes字段检查
else if (!string.IsNullOrEmpty(point.Notes) && point.Notes.StartsWith("GridType:"))
{
var gridTypeStr = point.Notes.Substring(9); // 去掉"GridType:"前缀
if (gridTypeStr == "Unknown")
gridStyle = GetRenderStyle(RenderColorType.GridUnknown);
else if (gridTypeStr == "Obstacle")
gridStyle = GetRenderStyle(RenderColorType.GridObstacle);
else
gridStyle = GetRenderStyle(RenderColorType.GridChannel);
}
// 门网格点使用50%透明度覆盖默认透明度
if (point.Name.Contains("门"))
{
gridStyle = new RenderStyle(gridStyle.Color, 0.5);
}
}
return new CircleMarker
{
Center = point.Position,
Normal = new Vector3D(0, 0, 1),
Radius = isGridVisualization ? GetRadiusForGridVisualization() : GetRadiusForPointType(point.Type),
Color = isGridVisualization ? gridStyle.Color : GetColorForPointType(point.Type),
Alpha = isGridVisualization ? gridStyle.Alpha : 1.0,
Filled = true,
PointType = point.Type,
GridPointType = isGridVisualization ? _gridPointType : GridPointType.Sphere, // 网格点使用配置的类型,其他点使用球形
SequenceNumber = point.Index, // 使用Index而不是任意序号
CreatedTime = DateTime.Now
};
}
/// <summary>
/// 获取网格可视化小球的半径(比正常路径点小)
/// </summary>
/// <returns>网格可视化半径</returns>
private double GetRadiusForGridVisualization()
{
// 使用标准尺寸的1/5作为网格点尺寸
double standardRadiusInMeters = GetStandardRadiusInMeters();
double radiusInMeters = standardRadiusInMeters / 5.0;
double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor();
return radiusInMeters * metersToModelUnits;
}
/// <summary>
/// 设置当前网格大小(米),用于自适应点大小计算
/// </summary>
/// <param name="gridSizeInMeters">网格大小(米)</param>
public void SetGridSize(double gridSizeInMeters)
{
_currentGridSizeInMeters = gridSizeInMeters;
}
/// <summary>
/// 设置车辆参数从PathPlanningManager同步
/// </summary>
/// <param name="vehicleLength">车辆长度(米)</param>
/// <param name="vehicleWidth">车辆宽度(米)</param>
/// <param name="vehicleHeight">车辆高度(米)</param>
/// <param name="safetyMargin">安全间隙(米)</param>
public void SetVehicleParameters(double vehicleLength, double vehicleWidth, double vehicleHeight, double safetyMargin)
{
_vehicleLength = vehicleLength;
_vehicleWidth = vehicleWidth;
_vehicleHeight = vehicleHeight;
_safetyMargin = safetyMargin;
// 车辆参数改变时刷新车辆空间模式下的所有普通路径
if (_visualizationMode == PathVisualizationMode.VehicleSpace)
{
RefreshNormalPaths();
}
}
#region
/// <summary>
/// 根据颜色类型获取对应的渲染样式(颜色+透明度)
/// </summary>
/// <param name="colorType">颜色类型</param>
/// <returns>对应的渲染样式</returns>
private RenderStyle GetRenderStyle(RenderColorType colorType)
{
switch (colorType)
{
case RenderColorType.StartPoint:
return new RenderStyle(Color.FromByteRGB(76, 175, 80), 0.9); // Material Green起点10%透明
case RenderColorType.EndPoint:
return new RenderStyle(Color.FromByteRGB(244, 67, 54), 0.9); // Material Red终点10%透明
case RenderColorType.WayPoint:
return new RenderStyle(Color.FromByteRGB(33, 150, 243), 0.9); // Material Blue路径点10%透明
case RenderColorType.Line:
return new RenderStyle(Color.FromByteRGB(255, 152, 0), 0.85); // Material Orange连线15%透明
case RenderColorType.VehicleSpace:
return new RenderStyle(Color.FromByteRGB(158, 158, 158), 0.4); // Material Grey车辆空间60%透明
case RenderColorType.PreviewPoint:
return new RenderStyle(Color.White, 0.7); // 白色预览点30%透明
case RenderColorType.PreviewLine:
return new RenderStyle(Color.FromByteRGB(128, 128, 128), 0.7); // 灰色预览连线30%透明
case RenderColorType.UnreachedEndPoint:
return new RenderStyle(Color.FromByteRGB(139, 0, 0), 0.7); // 深红色未到达终点30%透明
case RenderColorType.GridChannel:
return new RenderStyle(Color.FromByteRGB(129, 199, 132), 0.8); // Material Light Green网格通道20%透明
case RenderColorType.GridObstacle:
return new RenderStyle(Color.FromByteRGB(117, 117, 117), 0.8); // Material Grey网格障碍物20%透明
case RenderColorType.GridUnknown:
return new RenderStyle(Color.FromByteRGB(255, 112, 67), 0.8); // Material Deep Orange网格未知区域20%透明
default:
return new RenderStyle(Color.White, 1.0); // 默认白色,完全不透明
}
}
/// <summary>
/// 根据颜色类型获取对应的颜色(向后兼容方法)
/// </summary>
/// <param name="colorType">颜色类型</param>
/// <returns>对应的颜色</returns>
private Color GetRenderColor(RenderColorType colorType)
{
return GetRenderStyle(colorType).Color;
}
#endregion
/// <summary>
/// 获取连线半径
/// </summary>
/// <returns>连线半径</returns>
private double GetLineRadius()
{
// 获取标准尺寸(起点尺寸)
double standardRadiusInMeters = GetStandardRadiusInMeters();
// 连线半径为标准尺寸的40%
double lineRadiusInMeters = standardRadiusInMeters * 0.4;
return lineRadiusInMeters * UnitsConverter.GetMetersToUnitsConversionFactor();
}
/// <summary>
/// 添加路径点到指定路径
/// </summary>
/// <param name="pathId">路径ID</param>
/// <param name="newPoint">新的路径点</param>
/// <param name="insertIndex">插入位置索引,-1表示添加到末尾</param>
public void AddPointToPath(string pathId, PathPoint newPoint, int insertIndex = -1)
{
var visualization = GetPathVisualization(pathId);
if (visualization == null)
{
return;
}
try
{
var pathRoute = visualization.PathRoute;
if (insertIndex == -1)
{
// 添加到末尾
newPoint.Index = pathRoute.Points.Count;
pathRoute.Points.Add(newPoint);
}
else
{
// 插入到指定位置
pathRoute.Points.Insert(insertIndex, newPoint);
// 重新分配所有点的索引
ReindexPoints(pathRoute);
}
// 重建可视化
BuildVisualization(visualization);
RequestViewRefresh();
}
catch (Exception ex)
{
LogManager.Error($"[路径编辑] 添加路径点失败: {ex.Message}", ex);
}
}
/// <summary>
/// 从指定路径移除路径点
/// </summary>
/// <param name="pathId">路径ID</param>
/// <param name="pointIndex">要移除的点索引</param>
public void RemovePointFromPath(string pathId, int pointIndex)
{
var visualization = GetPathVisualization(pathId);
if (visualization == null)
{
return;
}
try
{
var pathRoute = visualization.PathRoute;
var pointToRemove = pathRoute.Points.FirstOrDefault(p => p.Index == pointIndex);
if (pointToRemove != null)
{
pathRoute.Points.Remove(pointToRemove);
// 重新分配索引
ReindexPoints(pathRoute);
// 重建可视化
BuildVisualization(visualization);
RequestViewRefresh();
}
else
{
LogManager.Warning($"[路径编辑] 未找到索引为 {pointIndex} 的路径点");
}
}
catch (Exception ex)
{
LogManager.Error($"[路径编辑] 移除路径点失败: {ex.Message}", ex);
}
}
/// <summary>
/// 更新指定路径中的路径点
/// </summary>
/// <param name="pathId">路径ID</param>
/// <param name="pointIndex">要更新的点索引</param>
/// <param name="updatedPoint">更新后的路径点</param>
public void UpdatePointInPath(string pathId, int pointIndex, PathPoint updatedPoint)
{
var visualization = GetPathVisualization(pathId);
if (visualization == null)
{
return;
}
try
{
var pathRoute = visualization.PathRoute;
var existingPoint = pathRoute.Points.FirstOrDefault(p => p.Index == pointIndex);
if (existingPoint != null)
{
// 保持索引不变
updatedPoint.Index = pointIndex;
// 替换点
var index = pathRoute.Points.IndexOf(existingPoint);
pathRoute.Points[index] = updatedPoint;
// 重建可视化
BuildVisualization(visualization);
RequestViewRefresh();
}
else
{
LogManager.Warning($"[路径编辑] 未找到索引为 {pointIndex} 的路径点");
}
}
catch (Exception ex)
{
LogManager.Error($"[路径编辑] 更新路径点失败: {ex.Message}", ex);
}
}
/// <summary>
/// 重新分配路径点索引
/// </summary>
/// <param name="pathRoute">路径对象</param>
private void ReindexPoints(PathRoute pathRoute)
{
// 按照当前在List中的位置重新分配连续索引
for (int i = 0; i < pathRoute.Points.Count; i++)
{
pathRoute.Points[i].Index = i;
}
}
/// <summary>
/// 获取路径可视化对象
/// </summary>
/// <param name="pathId">路径ID</param>
/// <returns>路径可视化对象如果不存在返回null</returns>
private PathVisualization GetPathVisualization(string pathId)
{
lock (_lockObject)
{
_pathVisualizations.TryGetValue(pathId, out var visualization);
return visualization;
}
}
#endregion
/// <summary>
/// 渲染预览点(灰色显示)
/// </summary>
/// <param name="previewPoint">预览点对象</param>
public void RenderPreviewPoint(PathPoint previewPoint)
{
try
{
if (previewPoint == null)
{
return;
}
lock (_lockObject)
{
// 获取适当的半径
double radius = GetRadiusForPointType(previewPoint.Type);
// 获取预览点样式(颜色+透明度)
var previewStyle = GetRenderStyle(RenderColorType.PreviewPoint);
// 创建预览点标记
_previewMarker = new CircleMarker
{
Position = previewPoint.Position,
Radius = radius,
Color = previewStyle.Color,
Alpha = previewStyle.Alpha,
IsVisible = true,
PathPoint = previewPoint
};
// 请求刷新视图
RequestViewRefresh();
}
}
catch (Exception ex)
{
LogManager.Error($"[预览点渲染] 渲染预览点失败: {ex.Message}", ex);
}
}
/// <summary>
/// 清除预览点
/// </summary>
public void ClearPreviewPoint()
{
try
{
lock (_lockObject)
{
if (_previewMarker != null || _previewLines.Count > 0)
{
_previewMarker = null;
_previewLines.Clear();
// 请求刷新视图
RequestViewRefresh();
}
}
}
catch (Exception ex)
{
LogManager.Error($"[预览点渲染] 清除预览点失败: {ex.Message}", ex);
}
}
/// <summary>
/// 渲染预览连线
/// </summary>
/// <param name="previewPoint">预览点</param>
/// <param name="pathPoints">当前路径点列表</param>
public void RenderPreviewLines(PathPoint previewPoint, List<PathPoint> pathPoints)
{
try
{
if (previewPoint == null || pathPoints == null || pathPoints.Count == 0)
{
return;
}
lock (_lockObject)
{
// 清除旧的预览连线
_previewLines.Clear();
// 找到预览点应该插入的最近线段
var nearestSegment = FindNearestLineSegment(previewPoint.Position, pathPoints);
if (nearestSegment.HasValue)
{
var (prevPoint, nextPoint) = nearestSegment.Value;
// 创建两条预览连线
// 1. 前一个点 -> 预览点
var line1 = new LineMarker
{
StartPoint = prevPoint.Position,
EndPoint = previewPoint.Position,
Color = GetRenderColor(RenderColorType.PreviewLine), // 预览连线颜色
Radius = GetLineRadius() // 使用与正常连线相同的直径
};
_previewLines.Add(line1);
// 2. 预览点 -> 后一个点
var line2 = new LineMarker
{
StartPoint = previewPoint.Position,
EndPoint = nextPoint.Position,
Color = GetRenderColor(RenderColorType.PreviewLine), // 预览连线颜色
Radius = GetLineRadius()
};
_previewLines.Add(line2);
}
else
{
LogManager.Warning("[预览连线渲染] 未找到合适的线段插入预览点");
}
// 请求刷新视图
RequestViewRefresh();
}
}
catch (Exception ex)
{
LogManager.Error($"[预览连线渲染] 渲染预览连线失败: {ex.Message}", ex);
}
}
/// <summary>
/// 清除预览连线
/// </summary>
public void ClearPreviewLines()
{
try
{
lock (_lockObject)
{
if (_previewLines.Count > 0)
{
_previewLines.Clear();
// 请求刷新视图
RequestViewRefresh();
}
}
}
catch (Exception ex)
{
LogManager.Error($"[预览连线渲染] 清除预览连线失败: {ex.Message}", ex);
}
}
/// <summary>
/// 渲染预览路径(用于修改路径点时的预览)
/// </summary>
/// <param name="previewRoute">包含预览修改的路径</param>
public void RenderPreviewPath(PathRoute previewRoute)
{
try
{
if (previewRoute == null || previewRoute.Points.Count == 0)
{
return;
}
lock (_lockObject)
{
// 只清理预览元素,不影响原有路径
ClearPreviewPoint();
ClearPreviewLines();
// 找到预览点及其索引
var previewPointIndex = -1;
PathPoint previewPoint = null;
for (int i = 0; i < previewRoute.Points.Count; i++)
{
if (previewRoute.Points[i].Name.Contains("_预览"))
{
previewPointIndex = i;
previewPoint = previewRoute.Points[i];
break;
}
}
if (previewPoint != null && previewPointIndex >= 0)
{
// 只渲染预览点
RenderPreviewPoint(previewPoint);
// 只渲染与预览点相关的连线
// 连接前一个点到预览点
if (previewPointIndex > 0)
{
var prevPoint = previewRoute.Points[previewPointIndex - 1];
var line1 = new LineMarker
{
StartPoint = prevPoint.Position,
EndPoint = previewPoint.Position,
Color = GetRenderColor(RenderColorType.PreviewLine), // 预览连线颜色
Radius = GetLineRadius()
};
_previewLines.Add(line1);
}
// 连接预览点到下一个点
if (previewPointIndex < previewRoute.Points.Count - 1)
{
var nextPoint = previewRoute.Points[previewPointIndex + 1];
var line2 = new LineMarker
{
StartPoint = previewPoint.Position,
EndPoint = nextPoint.Position,
Color = GetRenderColor(RenderColorType.PreviewLine), // 预览连线颜色
Radius = GetLineRadius()
};
_previewLines.Add(line2);
}
}
else
{
LogManager.Warning("[预览路径渲染] 未找到预览点,跳过渲染");
}
RequestViewRefresh();
}
}
catch (Exception ex)
{
LogManager.Error($"[预览路径渲染] 渲染预览路径失败: {ex.Message}", ex);
}
}
/// <summary>
/// 清理所有预览渲染
/// </summary>
public void ClearPreview()
{
try
{
lock (_lockObject)
{
// 清理预览点
ClearPreviewPoint();
// 清理预览连线
ClearPreviewLines();
RequestViewRefresh();
}
}
catch (Exception ex)
{
LogManager.Error($"[清理预览] 清理预览失败: {ex.Message}", ex);
}
}
#region
/// <returns>标准尺寸(米)</returns>
/// <summary>
/// 计算标准尺寸(起点尺寸),其他元素以此为基准按比例计算
/// </summary>
/// <returns>标准尺寸(米)</returns>
private double GetStandardRadiusInMeters()
{
// 起点尺寸为网格大小的100%,并限制在合理范围内
double standardRadius = _currentGridSizeInMeters * 1.0;
// 边界限制最小0.1米最大0.5米
return Math.Max(0.1, Math.Min(0.5, standardRadius));
}
public double GetRadiusForPointType(PathPointType pointType)
{
// 获取标准尺寸(起点尺寸)
double standardRadiusInMeters = GetStandardRadiusInMeters();
// 根据点类型应用比例系数
double baseRadiusInMeters;
if (pointType == PathPointType.WayPoint)
{
// 路径点为标准尺寸的80%
baseRadiusInMeters = standardRadiusInMeters * 0.8;
}
else
{
// 起点/终点使用标准尺寸100%
baseRadiusInMeters = standardRadiusInMeters * 1.0;
}
// 获取真实文档单位转换系数
double metersToModelUnits = UnitsConverter.GetMetersToUnitsConversionFactor();
// 转换为模型单位
double radiusInModelUnits = baseRadiusInMeters * metersToModelUnits;
return radiusInModelUnits;
}
/// <summary>
/// 根据路径点类型获取颜色
/// </summary>
public Color GetColorForPointType(PathPointType pointType)
{
switch (pointType)
{
case PathPointType.StartPoint:
return GetRenderColor(RenderColorType.StartPoint); // 起点绿色
case PathPointType.EndPoint:
return GetRenderColor(RenderColorType.EndPoint); // 终点红色
case PathPointType.WayPoint:
default:
return GetRenderColor(RenderColorType.WayPoint); // 路径中间点洋红色
}
}
/// <summary>
/// 计算两点间距离
/// </summary>
private double CalculateDistance(Point3D point1, Point3D point2)
{
var dx = point1.X - point2.X;
var dy = point1.Y - point2.Y;
var dz = point1.Z - point2.Z;
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
/// <summary>
/// 渲染车辆通行空间(矩形通道)- 使用Cuboid API简化实现
/// </summary>
/// <param name="graphics">图形上下文</param>
/// <param name="vehicleSpace">车辆通行空间标记</param>
private void RenderVehicleSpace(Graphics graphics, VehicleSpaceMarker vehicleSpace)
{
try
{
// 计算通道方向向量
var direction = new Vector3D(
vehicleSpace.EndPoint.X - vehicleSpace.StartPoint.X,
vehicleSpace.EndPoint.Y - vehicleSpace.StartPoint.Y,
vehicleSpace.EndPoint.Z - vehicleSpace.StartPoint.Z
);
// 计算通道长度
var length = Math.Sqrt(direction.X * direction.X + direction.Y * direction.Y + direction.Z * direction.Z);
if (length < 0.001) // 避免零长度线段
return;
// 归一化方向向量
direction = new Vector3D(direction.X / length, direction.Y / length, direction.Z / length);
// 计算垂直向量在XY平面内垂直于方向向量
var right = new Vector3D(-direction.Y, direction.X, 0);
// 如果方向向量垂直于XY平面则使用不同的right向量
if (Math.Abs(direction.Z) > 0.9)
{
right = new Vector3D(1, 0, 0);
}
// 归一化right向量
var rightLength = Math.Sqrt(right.X * right.X + right.Y * right.Y + right.Z * right.Z);
if (rightLength > 0.001)
{
right = new Vector3D(right.X / rightLength, right.Y / rightLength, right.Z / rightLength);
}
// 计算通道的边界框和向量
var halfWidth = vehicleSpace.Width / 2.0;
// 计算立方体原点(起点左下角)
var origin = new Point3D(
vehicleSpace.StartPoint.X - halfWidth * right.X,
vehicleSpace.StartPoint.Y - halfWidth * right.Y,
vehicleSpace.StartPoint.Z
);
// 定义立方体的三个轴向量
var xVector = new Vector3D(direction.X * length, direction.Y * length, direction.Z * length); // 长度方向
var yVector = new Vector3D(right.X * vehicleSpace.Width, right.Y * vehicleSpace.Width, right.Z * vehicleSpace.Width); // 宽度方向
var zVector = new Vector3D(0, 0, vehicleSpace.Height); // 高度方向
// 设置颜色和透明度
graphics.Color(vehicleSpace.Color, vehicleSpace.Alpha);
// 使用Cuboid API渲染长方体通道
graphics.Cuboid(origin, xVector, yVector, zVector, true);
}
catch (Exception ex)
{
LogManager.Error($"[车辆空间渲染] 渲染车辆通行空间失败: {ex.Message}", ex);
}
}
/// <summary>
/// 渲染点标记
/// </summary>
/// <param name="graphics">图形上下文</param>
/// <param name="pointMarker">点标记</param>
private void RenderPointMarker(Graphics graphics, CircleMarker pointMarker)
{
switch (pointMarker.GridPointType)
{
case GridPointType.Rectangle:
// 渲染立方体
RenderCubeMarker(graphics, pointMarker);
break;
case GridPointType.Circle:
// 渲染圆
RenderCircleMarker(graphics, pointMarker);
break;
case GridPointType.Sphere:
default:
// 渲染球形(默认)
graphics.Sphere(pointMarker.Center, pointMarker.Radius);
break;
}
}
/// <summary>
/// 渲染立方体标记
/// </summary>
/// <param name="graphics">图形上下文</param>
/// <param name="pointMarker">点标记</param>
private void RenderCubeMarker(Graphics graphics, CircleMarker pointMarker)
{
// 计算正方形的边长(直径)
double sideLength = pointMarker.Radius * 2;
// 定义正方形的两个轴向量沿X、Y轴
var xVector = new Vector3D(sideLength, 0, 0);
var yVector = new Vector3D(0, sideLength, 0);
// 计算正方形原点(中心点减去各轴向量的一半),略微抬高以避免与模型重叠
var origin = new Point3D(
pointMarker.Center.X - pointMarker.Radius,
pointMarker.Center.Y - pointMarker.Radius,
pointMarker.Center.Z + 0.01
);
// 使用Rectangle API渲染正方形
graphics.Rectangle(origin, xVector, yVector, pointMarker.Filled);
}
/// <summary>
/// 渲染圆形标记
/// </summary>
/// <param name="graphics">图形上下文</param>
/// <param name="pointMarker">点标记</param>
private void RenderCircleMarker(Graphics graphics, CircleMarker pointMarker)
{
// 调整中心点位置,略微抬高以避免与模型重叠
Point3D adjustCenterPoint = new Point3D(
pointMarker.Center.X,
pointMarker.Center.Y,
pointMarker.Center.Z + 0.01
);
// 使用Circle API渲染圆形
graphics.Circle(adjustCenterPoint, pointMarker.Normal, pointMarker.Radius, true);
}
/// <summary>
/// 渲染方块标记(切点)
/// </summary>
/// <param name="graphics">图形上下文</param>
/// <param name="marker">方块标记</param>
private void RenderSquareMarker(Graphics graphics, SquareMarker marker)
{
// 计算立方体的三个轴向量
var halfSize = marker.Size / 2.0;
// X轴向量沿X方向
var xVector = new Vector3D(marker.Size, 0, 0);
// Y轴向量沿Y方向
var yVector = new Vector3D(0, marker.Size, 0);
// Z轴向量沿Z方向高度很小形成扁平立方体
var zVector = new Vector3D(0, 0, marker.Size * 0.2);
// 计算立方体原点(中心点偏移)
var origin = new Point3D(
marker.Center.X - halfSize,
marker.Center.Y - halfSize,
marker.Center.Z - zVector.Z / 2
);
// 使用Cuboid API绘制方块
graphics.Cuboid(origin, xVector, yVector, zVector, true);
}
/// <summary>
/// 渲染连线标记(支持直线段和圆弧段)
/// </summary>
/// <param name="graphics">图形上下文</param>
/// <param name="lineMarker">连线标记</param>
private void RenderLineMarker(Graphics graphics, LineMarker lineMarker)
{
if (lineMarker.SegmentType == PathSegmentType.Arc &&
lineMarker.SampledPoints != null &&
lineMarker.SampledPoints.Count >= 2)
{
// 圆弧段:多段圆柱体
for (int i = 0; i < lineMarker.SampledPoints.Count - 1; i++)
{
graphics.Cylinder(lineMarker.SampledPoints[i], lineMarker.SampledPoints[i + 1], lineMarker.Radius);
}
}
else
{
// 直线段:单个圆柱体
graphics.Cylinder(lineMarker.StartPoint, lineMarker.EndPoint, lineMarker.Radius);
}
}
/// <summary>
/// 请求视图刷新(带防抖机制)
/// </summary>
private void RequestViewRefresh()
{
try
{
// 使用静态变量实现简单的防抖机制
var now = DateTime.Now;
if ((now - _lastRefreshTime).TotalMilliseconds < 50) // 最小间隔50ms
{
return; // 忽略过于频繁的刷新请求
}
_lastRefreshTime = now;
if (Application.ActiveDocument?.ActiveView != null)
{
Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.Render);
}
}
catch (Exception ex)
{
LogManager.Error($"[视图刷新] 失败: {ex.Message}", ex);
}
}
/// <summary>
/// 找到预览点应该插入的最近线段
/// </summary>
/// <param name="previewPosition">预览点位置</param>
/// <param name="pathPoints">路径点列表</param>
/// <returns>最近线段的前后两个点如果找不到则返回null</returns>
private (PathPoint prevPoint, PathPoint nextPoint)? FindNearestLineSegment(Point3D previewPosition, List<PathPoint> pathPoints)
{
if (pathPoints == null || pathPoints.Count < 2)
{
return null;
}
// 按索引排序路径点
var sortedPoints = pathPoints.OrderBy(p => p.Index).ToList();
double minDistance = double.MaxValue;
(PathPoint prevPoint, PathPoint nextPoint)? nearestSegment = null;
// 遍历相邻的路径点对,找到距离预览点最近的线段
for (int i = 0; i < sortedPoints.Count - 1; i++)
{
var currentPoint = sortedPoints[i];
var nextPoint = sortedPoints[i + 1];
// 计算预览点到线段的距离
var distance = CalculatePointToLineSegmentDistance(previewPosition, currentPoint.Position, nextPoint.Position);
if (distance < minDistance)
{
minDistance = distance;
nearestSegment = (currentPoint, nextPoint);
}
}
return nearestSegment;
}
/// <summary>
/// 计算点到线段的距离
/// </summary>
/// <param name="point">目标点</param>
/// <param name="lineStart">线段起点</param>
/// <param name="lineEnd">线段终点</param>
/// <returns>点到线段的最短距离</returns>
private double CalculatePointToLineSegmentDistance(Point3D point, Point3D lineStart, Point3D lineEnd)
{
// 线段向量
var lineVector = new Point3D(lineEnd.X - lineStart.X, lineEnd.Y - lineStart.Y, lineEnd.Z - lineStart.Z);
// 点到线段起点的向量
var pointVector = new Point3D(point.X - lineStart.X, point.Y - lineStart.Y, point.Z - lineStart.Z);
// 计算线段长度的平方
var lineLengthSquared = lineVector.X * lineVector.X + lineVector.Y * lineVector.Y + lineVector.Z * lineVector.Z;
if (lineLengthSquared == 0)
{
// 线段退化为点,返回点到点的距离
return CalculateDistance(point, lineStart);
}
// 计算投影参数t
var t = (pointVector.X * lineVector.X + pointVector.Y * lineVector.Y + pointVector.Z * lineVector.Z) / lineLengthSquared;
// 将t限制在[0,1]范围内
t = Math.Max(0, Math.Min(1, t));
// 计算线段上最近点
var closestPoint = new Point3D(
lineStart.X + t * lineVector.X,
lineStart.Y + t * lineVector.Y,
lineStart.Z + t * lineVector.Z
);
// 返回点到最近点的距离
return CalculateDistance(point, closestPoint);
}
private static DateTime _lastRefreshTime = DateTime.MinValue;
#endregion
}
/// <summary>
/// 切点类型枚举
/// </summary>
public enum TangentPointType
{
/// <summary>
/// 进入切点Ts
/// </summary>
EntryTangent,
/// <summary>
/// 退出切点Te
/// </summary>
ExitTangent
}
/// <summary>
/// 方块标记数据结构(用于切点)
/// </summary>
public class SquareMarker
{
/// <summary>
/// 中心坐标
/// </summary>
public Point3D Center { get; set; }
/// <summary>
/// 方块面法向量
/// </summary>
public Vector3D Normal { get; set; }
/// <summary>
/// 方块边长
/// </summary>
public double Size { get; set; }
/// <summary>
/// 方块颜色
/// </summary>
public Color Color { get; set; }
/// <summary>
/// 不透明度 (1.0 = 完全不透明, 0.0 = 完全透明)
/// </summary>
public double Alpha { get; set; }
/// <summary>
/// 所属边ID
/// </summary>
public string EdgeId { get; set; }
/// <summary>
/// 切点类型
/// </summary>
public TangentPointType TangentType { get; set; }
public override string ToString()
{
return $"SquareMarker[类型={TangentType}, 中心=({Center.X:F2},{Center.Y:F2},{Center.Z:F2}), 边长={Size:F2}]";
}
}
/// <summary>
/// 圆形标记数据结构
/// </summary>
public class CircleMarker
{
/// <summary>
/// 圆心坐标
/// </summary>
public Point3D Center { get; set; }
/// <summary>
/// 圆面法向量
/// </summary>
public Vector3D Normal { get; set; }
/// <summary>
/// 圆形半径
/// </summary>
public double Radius { get; set; }
/// <summary>
/// 圆形颜色
/// </summary>
public Color Color { get; set; }
/// <summary>
/// 不透明度 (1.0 = 完全不透明, 0.0 = 完全透明)
/// </summary>
public double Alpha { get; set; }
/// <summary>
/// 是否填充
/// </summary>
public bool Filled { get; set; }
/// <summary>
/// 路径点类型
/// </summary>
public PathPointType PointType { get; set; }
/// <summary>
/// 网格点类型
/// </summary>
public GridPointType GridPointType { get; set; } = GridPointType.Sphere;
/// <summary>
/// 序号
/// </summary>
public int SequenceNumber { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedTime { get; set; }
/// <summary>
/// 是否可见
/// </summary>
public bool IsVisible { get; set; }
/// <summary>
/// 是否为不在路径上的控制点(半透明)
/// </summary>
public bool IsOffPath { get; set; } = false;
/// <summary>
/// 位置Center的别名用于预览点兼容性
/// </summary>
public Point3D Position
{
get { return Center; }
set { Center = value; }
}
/// <summary>
/// 关联的路径点对象
/// </summary>
public PathPoint PathPoint { get; set; }
public override string ToString()
{
return $"CircleMarker[序号={SequenceNumber}, 类型={PointType}, 中心=({Center.X:F2},{Center.Y:F2},{Center.Z:F2}), 半径={Radius:F2}]";
}
}
}