修改了通道网格生成的高度设置,能在斜面上生成网格,可视化也对了
This commit is contained in:
parent
cd74f857a2
commit
8a95820fca
@ -80,14 +80,6 @@ Eight predefined categories with inheritance from parent to child nodes:
|
||||
- 门 (Doors), 电梯 (Elevators), 楼梯 (Stairs), 通道 (Channels)
|
||||
- 障碍物 (Obstacles), 装卸区 (Loading Zones), 停车区 (Parking), 检查点 (Checkpoints)
|
||||
|
||||
## Development Guidelines
|
||||
|
||||
### Claude Code Work Delegation Principle
|
||||
|
||||
- **任何具体的开发工作,全部让子代理完成** - All concrete development work must be delegated to specialized sub-agents
|
||||
- Claude Code主要负责需求理解、任务分解和工作协调,不直接进行代码编写
|
||||
- 使用专业子代理处理具体实现:`navisworks-feature-developer`、`navisworks-api-researcher`、`navisworks-ui-designer`、`technical-architect`、`project-task-manager`
|
||||
|
||||
### Language and Communication
|
||||
|
||||
- **使用中文进行所有交流和代码注释** - Primary language for user interaction and code documentation
|
||||
@ -203,6 +195,5 @@ public class PathClickToolPlugin : ToolPlugin { }
|
||||
- **2026 Features**: Test advanced animation capabilities, enhanced collision detection, and improved model handling
|
||||
- 在编码中,不要用回退或向后兼容的思路和步骤
|
||||
- 程序的日志在:C:\ProgramData\Autodesk\Navisworks Manage 2026\NavisworksTransport\logs\debug.log
|
||||
- 不要搞向后兼容
|
||||
- 使用agent完成任务前,一定要先用Plan模式设计好方案和任务清单,并征得我同意。
|
||||
- 网格坐标代表的是网格单元的左下角,而不是中心点!
|
||||
@ -163,8 +163,6 @@
|
||||
<Compile Include="src\PathPlanning\ChannelBasedGridBuilder.cs" />
|
||||
<Compile Include="src\PathPlanning\VerticalScanProcessor.cs" />
|
||||
<Compile Include="src\PathPlanning\PathOptimizer.cs" />
|
||||
<Compile Include="src\PathPlanning\GridCacheKey.cs" />
|
||||
<Compile Include="src\PathPlanning\GridCache.cs" />
|
||||
<Compile Include="src\PathPlanning\GridMapCacheKey.cs" />
|
||||
<Compile Include="src\PathPlanning\GridMapCache.cs" />
|
||||
|
||||
|
||||
@ -2,6 +2,14 @@
|
||||
|
||||
## 功能点
|
||||
|
||||
### [2025/09/28]
|
||||
|
||||
1. [x] (优化)识别表面不平的通道,给网格正确的表面z值
|
||||
|
||||
### [2025/09/27]
|
||||
|
||||
1. [x] (优化)为生成的网格地图增加缓存,提高路径规划性能
|
||||
|
||||
### [2025/09/15]
|
||||
|
||||
1. [x] (功能)修改碰撞检测报告,增加碰撞构件的数量和清单
|
||||
|
||||
@ -1113,7 +1113,7 @@ namespace NavisworksTransport
|
||||
var document = Application.ActiveDocument;
|
||||
|
||||
// 获取所有可通行的物流模型项
|
||||
var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems(document);
|
||||
var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems();
|
||||
|
||||
if (allChannelItems.Count == 0)
|
||||
{
|
||||
|
||||
@ -879,7 +879,6 @@ namespace NavisworksTransport
|
||||
try
|
||||
{
|
||||
gridMap = gridMapGenerator.GenerateFromBIM(
|
||||
document,
|
||||
bounds,
|
||||
gridSize,
|
||||
vehicleRadius,
|
||||
@ -1125,7 +1124,7 @@ namespace NavisworksTransport
|
||||
var document = Application.ActiveDocument;
|
||||
if (document != null)
|
||||
{
|
||||
var allLogisticsItems = CategoryAttributeManager.GetAllLogisticsItems(document);
|
||||
var allLogisticsItems = CategoryAttributeManager.GetAllLogisticsItems();
|
||||
var channelItems = CategoryAttributeManager.FilterByLogisticsType(allLogisticsItems, CategoryAttributeManager.LogisticsElementType.通道);
|
||||
_walkableAreas.AddRange(channelItems);
|
||||
LogManager.Info($"[SelectChannels] 通过CategoryAttributeManager直接筛选到 {channelItems.Count} 个通道");
|
||||
@ -2239,7 +2238,7 @@ namespace NavisworksTransport
|
||||
LogManager.Info("[通道自动选择] 开始搜索可通行的物流模型");
|
||||
|
||||
// 使用CategoryAttributeManager统一接口获取物流项目
|
||||
var allLogisticsItemsCollection = CategoryAttributeManager.GetAllLogisticsItems(document);
|
||||
var allLogisticsItemsCollection = CategoryAttributeManager.GetAllLogisticsItems();
|
||||
LogManager.Info($"[通道自动选择] CategoryAttributeManager找到 {allLogisticsItemsCollection.Count} 个具有物流属性的模型项");
|
||||
|
||||
// 筛选出可通行的物流模型
|
||||
@ -2562,7 +2561,7 @@ namespace NavisworksTransport
|
||||
if (document?.Models != null)
|
||||
{
|
||||
// 使用CategoryAttributeManager统一接口获取物流项目
|
||||
var logisticsItemsCollection = CategoryAttributeManager.GetAllLogisticsItems(document);
|
||||
var logisticsItemsCollection = CategoryAttributeManager.GetAllLogisticsItems();
|
||||
LogManager.WriteLog($"[搜索通道] CategoryAttributeManager找到 {logisticsItemsCollection.Count} 个物流模型");
|
||||
|
||||
if (logisticsItemsCollection.Count > 0)
|
||||
@ -2873,7 +2872,7 @@ namespace NavisworksTransport
|
||||
var document = Application.ActiveDocument;
|
||||
if (document != null)
|
||||
{
|
||||
var foundChannelsCollection = CategoryAttributeManager.GetAllLogisticsItems(document);
|
||||
var foundChannelsCollection = CategoryAttributeManager.GetAllLogisticsItems();
|
||||
foreach (ModelItem item in foundChannelsCollection)
|
||||
{
|
||||
channelItems.Add(item);
|
||||
@ -3291,7 +3290,7 @@ namespace NavisworksTransport
|
||||
|
||||
// 计算网格单元格的世界中心位置
|
||||
var gridPos = new NavisworksTransport.PathPlanning.GridPoint2D(x, y);
|
||||
var gridCorner = gridMap.GridToWorld3D(gridPos, cell.WorldPosition.Z);
|
||||
var gridCorner = gridMap.GridToWorld3D(gridPos);
|
||||
var gridCenter = new Point3D(
|
||||
gridCorner.X + gridMap.CellSize / 2,
|
||||
gridCorner.Y + gridMap.CellSize / 2,
|
||||
|
||||
@ -115,7 +115,7 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
// 准备属性字典
|
||||
var properties = new System.Collections.Generic.Dictionary<string, string>
|
||||
var properties = new Dictionary<string, string>
|
||||
{
|
||||
{ LogisticsProperties.TYPE, elementType.ToString() },
|
||||
{ LogisticsProperties.TRAVERSABLE, isTraversable ? "是" : "否" },
|
||||
@ -126,7 +126,7 @@ namespace NavisworksTransport
|
||||
};
|
||||
|
||||
// 准备内部名称字典
|
||||
var propertyInternalNames = new System.Collections.Generic.Dictionary<string, string>
|
||||
var propertyInternalNames = new Dictionary<string, string>
|
||||
{
|
||||
{ LogisticsProperties.TYPE, elementType.ToString() + "_Internal" },
|
||||
{ LogisticsProperties.TRAVERSABLE, "Traversable_Internal" },
|
||||
@ -212,22 +212,11 @@ namespace NavisworksTransport
|
||||
/// <param name="elementType">物流元素类型</param>
|
||||
/// <param name="document">文档,如果为null则使用ActiveDocument</param>
|
||||
/// <returns>指定类型的物流项目列表</returns>
|
||||
public static List<ModelItem> GetLogisticsItemsByType(LogisticsElementType elementType, Document document = null)
|
||||
public static List<ModelItem> GetLogisticsItemsByType(LogisticsElementType elementType)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果没有提供文档,使用ActiveDocument
|
||||
if (document == null)
|
||||
document = Application.ActiveDocument;
|
||||
|
||||
// 检查文档有效性
|
||||
if (document?.Models == null)
|
||||
{
|
||||
LogManager.Warning($"[CategoryAttributeManager] 无法获取文档模型");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
|
||||
// 直接使用 Search API 搜索整个文档
|
||||
// 使用 Search API 搜索当前活动文档
|
||||
using (var search = new Search())
|
||||
{
|
||||
// 搜索整个文档的根项及其所有后代
|
||||
@ -246,7 +235,7 @@ namespace NavisworksTransport
|
||||
.EqualValue(VariantData.FromDisplayString(elementType.ToString())));
|
||||
|
||||
// 执行搜索并返回列表
|
||||
return search.FindAll(document, false).ToList();
|
||||
return search.FindAll(Application.ActiveDocument, false).ToList();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -358,7 +347,7 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
/// <param name="document">Navisworks文档</param>
|
||||
/// <returns>具有物流属性的模型项集合</returns>
|
||||
public static ModelItemCollection GetAllLogisticsItems(Document document)
|
||||
public static ModelItemCollection GetAllLogisticsItems()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -369,7 +358,7 @@ namespace NavisworksTransport
|
||||
searchConditions.Clear();
|
||||
var hasLogisticsCondition = SearchCondition.HasCategoryByDisplayName(LogisticsCategories.LOGISTICS);
|
||||
searchConditions.Add(hasLogisticsCondition);
|
||||
return search.FindAll(document, false);
|
||||
return search.FindAll(Application.ActiveDocument, false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -384,9 +373,9 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
/// <param name="document">Navisworks文档</param>
|
||||
/// <returns>可通行的物流模型项集合</returns>
|
||||
public static ModelItemCollection GetAllTraversableLogisticsItems(Document document)
|
||||
public static ModelItemCollection GetAllTraversableLogisticsItems()
|
||||
{
|
||||
var allLogisticsItems = GetAllLogisticsItems(document);
|
||||
var allLogisticsItems = GetAllLogisticsItems();
|
||||
return FilterTraversableItems(allLogisticsItems);
|
||||
}
|
||||
|
||||
|
||||
@ -1354,12 +1354,12 @@ namespace NavisworksTransport.PathPlanning
|
||||
var actualEndGridY = (int)Math.Floor(lastNode.Position.Y / cellSizeInMeters);
|
||||
var actualEndGrid = new GridPoint2D(actualEndGridX, actualEndGridY);
|
||||
var actualEndCell = gridMap.GetCell(actualEndGrid);
|
||||
var actualEndWorld = gridMap.GridToWorld3D(actualEndGrid, actualEndCell?.WorldPosition.Z ?? 0);
|
||||
var actualEndWorld = gridMap.GridToWorld3D(actualEndGrid);
|
||||
|
||||
var originalEndCell = gridMap.GetCell(endGrid);
|
||||
var originalEndWorld = gridMap.GridToWorld3D(endGrid, originalEndCell?.WorldPosition.Z ?? 0);
|
||||
var originalEndWorld = gridMap.GridToWorld3D(endGrid);
|
||||
var startWorldCell = gridMap.GetCell(startGrid);
|
||||
var startWorld = gridMap.GridToWorld3D(startGrid, startWorldCell?.WorldPosition.Z ?? 0);
|
||||
var startWorld = gridMap.GridToWorld3D(startGrid);
|
||||
|
||||
var totalDistance = CalculateDistance(startWorld, originalEndWorld);
|
||||
var actualDistance = CalculateDistance(startWorld, actualEndWorld);
|
||||
|
||||
@ -56,16 +56,13 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <param name="gridSize">网格单元格大小</param>
|
||||
/// <param name="document">当前文档</param>
|
||||
/// <returns>通道覆盖信息</returns>
|
||||
public ChannelCoverage BuildChannelCoverage(double gridSize, Document document = null)
|
||||
public ChannelCoverage BuildChannelCoverage(double gridSize)
|
||||
{
|
||||
if (document == null)
|
||||
document = Application.ActiveDocument;
|
||||
|
||||
LogManager.Info("[通道网格构建器] 开始构建通道覆盖网格");
|
||||
|
||||
// 1. 获取所有通道物品
|
||||
var channelItems = CategoryAttributeManager.GetLogisticsItemsByType(
|
||||
CategoryAttributeManager.LogisticsElementType.通道, document);
|
||||
CategoryAttributeManager.LogisticsElementType.通道);
|
||||
|
||||
if (!channelItems.Any())
|
||||
{
|
||||
@ -119,34 +116,13 @@ namespace NavisworksTransport.PathPlanning
|
||||
LogManager.Info($"[通道网格构建器] 投影通道: {channel.DisplayName}");
|
||||
|
||||
// 提取三角形几何
|
||||
var triangles = GeometryHelper.ExtractTrianglesOptimized(channel);
|
||||
var triangles = GeometryHelper.ExtractTriangles(channel);
|
||||
if (triangles.Count == 0)
|
||||
{
|
||||
LogManager.Warning($"[通道网格构建器] 未能从 '{channel.DisplayName}' 提取到三角形");
|
||||
return;
|
||||
}
|
||||
|
||||
// 分析通道类型
|
||||
var channelType = AnalyzeChannelType(triangles);
|
||||
|
||||
// 统计三角形方向分布(用于调试)
|
||||
int upFaces = 0, inclinedFaces = 0, downFaces = 0, sideFaces = 0;
|
||||
foreach (var triangle in triangles)
|
||||
{
|
||||
var normal = ComputeTriangleNormal(triangle);
|
||||
if (normal.Z > 0.7)
|
||||
upFaces++;
|
||||
else if (normal.Z > 0.2)
|
||||
inclinedFaces++;
|
||||
else if (normal.Z < -0.2)
|
||||
downFaces++;
|
||||
else
|
||||
sideFaces++;
|
||||
}
|
||||
|
||||
LogManager.Info($"[通道网格构建器] 通道类型: {channelType}");
|
||||
LogManager.Info($"[通道网格构建器] 三角形分布: 朝上={upFaces}, 朝上斜面={inclinedFaces}, 朝下={downFaces}, 侧面={sideFaces}, 总计={triangles.Count}");
|
||||
|
||||
|
||||
// 统一投影处理
|
||||
int processedTriangles = 0;
|
||||
foreach (var triangle in triangles)
|
||||
@ -154,18 +130,11 @@ namespace NavisworksTransport.PathPlanning
|
||||
var normal = ComputeTriangleNormal(triangle);
|
||||
|
||||
// 根据法向量决定处理方式
|
||||
if (normal.Z > 0.7) // 朝上的水平面
|
||||
if (normal.Z > 0) // 朝上的面
|
||||
{
|
||||
RasterizeTriangleToGrid(gridMap, triangle);
|
||||
processedTriangles++;
|
||||
}
|
||||
else if (normal.Z > 0.2) // 可行走的斜面(楼梯、坡道)
|
||||
{
|
||||
// 对于斜面,也进行投影,适用于楼梯等倾斜通道
|
||||
// [TODO] 斜面应该用不同的计算顶面高度方法
|
||||
RasterizeTriangleToGrid(gridMap, triangle);
|
||||
processedTriangles++;
|
||||
}
|
||||
// 垂直面和朝下的面不投影
|
||||
}
|
||||
|
||||
@ -205,6 +174,15 @@ namespace NavisworksTransport.PathPlanning
|
||||
return normal;
|
||||
}
|
||||
|
||||
private double GetHeightAtPoint(Triangle3D triangle, double x, double y)
|
||||
{
|
||||
var normal = ComputeTriangleNormal(triangle);
|
||||
|
||||
// 使用平面方程计算点(x,y)在三角形平面上的Z值
|
||||
double height = triangle.Point1.Z - (normal.X * (x - triangle.Point1.X) + normal.Y * (y - triangle.Point1.Y)) / normal.Z;
|
||||
return height;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 将三角形光栅化到网格
|
||||
@ -222,9 +200,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
var minGrid = gridMap.WorldToGrid(new Point3D(minX, minY, 0));
|
||||
var maxGrid = gridMap.WorldToGrid(new Point3D(maxX, maxY, 0));
|
||||
|
||||
// 计算三角形的平均高度作为通道顶面高度
|
||||
double triangleHeight = (triangle.Point1.Z + triangle.Point2.Z + triangle.Point3.Z) / 3.0;
|
||||
|
||||
// 遍历边界框内的所有网格点
|
||||
for (int x = minGrid.X; x <= maxGrid.X; x++)
|
||||
{
|
||||
@ -233,13 +208,30 @@ namespace NavisworksTransport.PathPlanning
|
||||
var gridPos = new GridPoint2D(x, y);
|
||||
if (gridMap.IsValidGridPosition(gridPos))
|
||||
{
|
||||
var worldPos = gridMap.GridToWorld3D(gridPos, 0); // 2D检测,Z坐标不重要
|
||||
var worldPos = gridMap.GridToWorld3D(gridPos);
|
||||
|
||||
// 检查点是否在三角形内部
|
||||
if (GeometryHelper.IsPointInTriangle2D(worldPos, triangle))
|
||||
{
|
||||
// 传递实际的三角形高度
|
||||
SetCellAsChannel(gridMap, gridPos, null, triangleHeight);
|
||||
// 计算网格中心点在三角形平面上的精确高度
|
||||
double gridCenterHeight = GetHeightAtPoint(triangle, worldPos.X, worldPos.Y);
|
||||
|
||||
// 调试日志:打印生成的网格坐标和高度
|
||||
LogManager.Debug($"[调试] 生成通道网格: ({gridPos.X}, {gridPos.Y}) 高度: {gridCenterHeight:F2}");
|
||||
|
||||
// 直接设置网格属性
|
||||
var cell = new GridCell
|
||||
{
|
||||
IsWalkable = true,
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.通道,
|
||||
IsInChannel = true,
|
||||
PassableHeights = new List<HeightInterval>(),
|
||||
ChannelType = ChannelType.Corridor,
|
||||
WorldPosition = new Point3D(worldPos.X, worldPos.Y, gridCenterHeight),
|
||||
RelatedModelItem = null
|
||||
};
|
||||
cell.Cost = cell.GetCost();
|
||||
gridMap.Cells[gridPos.X, gridPos.Y] = cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -247,17 +239,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置网格单元为通道类型
|
||||
/// </summary>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <param name="gridPos">网格位置</param>
|
||||
/// <param name="relatedItem">关联的模型物品</param>
|
||||
private void SetCellAsChannel(GridMap gridMap, GridPoint2D gridPos, ModelItem relatedItem, double channelHeight = 0.0)
|
||||
{
|
||||
// 使用GridMap提供的方法设置通道单元格,传递实际高度
|
||||
gridMap.SetCellAsChannel(gridPos, ChannelType.Corridor, relatedItem, channelHeight);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -275,50 +256,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分析通道几何类型
|
||||
/// </summary>
|
||||
/// <param name="triangles">三角形列表</param>
|
||||
/// <returns>通道几何类型</returns>
|
||||
private ChannelGeometryType AnalyzeChannelType(List<Triangle3D> triangles)
|
||||
{
|
||||
if (triangles.Count == 0)
|
||||
return ChannelGeometryType.Complex;
|
||||
|
||||
int upwardHorizontalCount = 0; // 朝上的水平面
|
||||
int upwardInclinedCount = 0; // 朝上的斜面
|
||||
int upwardCount = 0; // 所有朝上的面
|
||||
|
||||
foreach (var triangle in triangles)
|
||||
{
|
||||
var normal = ComputeTriangleNormal(triangle);
|
||||
|
||||
// 只统计朝上的面(与投影逻辑一致)
|
||||
if (normal.Z > 0.7)
|
||||
{
|
||||
upwardHorizontalCount++;
|
||||
upwardCount++;
|
||||
}
|
||||
else if (normal.Z > 0.2) // 朝上的斜面
|
||||
{
|
||||
upwardInclinedCount++;
|
||||
upwardCount++;
|
||||
}
|
||||
// 忽略朝下和侧面的三角形
|
||||
}
|
||||
|
||||
// 基于朝上三角形的比例判断类型
|
||||
if (upwardCount == 0)
|
||||
return ChannelGeometryType.Complex;
|
||||
|
||||
if (upwardHorizontalCount > upwardCount * 0.6)
|
||||
return ChannelGeometryType.Horizontal; // 主要是水平面
|
||||
else if (upwardInclinedCount > upwardCount * 0.3)
|
||||
return ChannelGeometryType.Inclined; // 有较多斜面
|
||||
else
|
||||
return ChannelGeometryType.Complex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格式化边界框信息用于日志
|
||||
/// </summary>
|
||||
|
||||
@ -655,7 +655,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
LogManager.Info($"[COM几何射线] 开始从ModelItem提取三角形几何数据: {channel.DisplayName}");
|
||||
|
||||
// 使用COM API提取三角形几何数据
|
||||
var triangles = GeometryHelper.ExtractTrianglesOptimized(channel);
|
||||
var triangles = GeometryHelper.ExtractTriangles(channel);
|
||||
if (triangles.Count == 0)
|
||||
{
|
||||
LogManager.Warning($"[COM几何射线] 未能从ModelItem提取到三角形数据: {channel.DisplayName}");
|
||||
|
||||
@ -1,331 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Roy_T.AStar.Grids;
|
||||
|
||||
namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存项,包含网格和访问时间信息
|
||||
/// </summary>
|
||||
public class GridCacheItem
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存的A*网格
|
||||
/// </summary>
|
||||
public Grid Grid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最后访问时间
|
||||
/// </summary>
|
||||
public DateTime LastAccessTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreatedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 访问次数
|
||||
/// </summary>
|
||||
public int AccessCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
public GridCacheItem(Grid grid)
|
||||
{
|
||||
Grid = grid ?? throw new ArgumentNullException(nameof(grid));
|
||||
LastAccessTime = DateTime.Now;
|
||||
CreatedTime = DateTime.Now;
|
||||
AccessCount = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录访问
|
||||
/// </summary>
|
||||
public void RecordAccess()
|
||||
{
|
||||
LastAccessTime = DateTime.Now;
|
||||
AccessCount++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网格缓存统计信息
|
||||
/// </summary>
|
||||
public class GridCacheStatistics
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存命中次数
|
||||
/// </summary>
|
||||
public long HitCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 缓存未命中次数
|
||||
/// </summary>
|
||||
public long MissCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前缓存项数量
|
||||
/// </summary>
|
||||
public int CurrentCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 最大缓存项数量
|
||||
/// </summary>
|
||||
public int MaxCapacity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 被驱逐的缓存项数量
|
||||
/// </summary>
|
||||
public long EvictedCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 缓存命中率
|
||||
/// </summary>
|
||||
public double HitRate => HitCount + MissCount > 0 ? (double)HitCount / (HitCount + MissCount) : 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// 缓存使用率
|
||||
/// </summary>
|
||||
public double UsageRate => MaxCapacity > 0 ? (double)CurrentCount / MaxCapacity : 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// 重置统计信息
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
HitCount = 0;
|
||||
MissCount = 0;
|
||||
EvictedCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成统计报告
|
||||
/// </summary>
|
||||
public string GenerateReport()
|
||||
{
|
||||
return $"网格缓存统计 - 命中率: {HitRate:P2} ({HitCount}命中/{MissCount}未命中), " +
|
||||
$"使用率: {UsageRate:P2} ({CurrentCount}/{MaxCapacity}), " +
|
||||
$"驱逐次数: {EvictedCount}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网格缓存管理器
|
||||
/// 使用LRU策略管理A*网格缓存,支持线程安全访问
|
||||
/// </summary>
|
||||
public class GridCache
|
||||
{
|
||||
private readonly ConcurrentDictionary<GridCacheKey, GridCacheItem> _cache;
|
||||
private readonly object _cleanupLock = new object();
|
||||
private readonly int _maxCapacity;
|
||||
private readonly GridCacheStatistics _statistics;
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存统计信息
|
||||
/// </summary>
|
||||
public GridCacheStatistics Statistics => _statistics;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="maxCapacity">最大缓存容量,默认20个网格</param>
|
||||
public GridCache(int maxCapacity = 20)
|
||||
{
|
||||
if (maxCapacity <= 0)
|
||||
throw new ArgumentException("缓存容量必须大于0", nameof(maxCapacity));
|
||||
|
||||
_maxCapacity = maxCapacity;
|
||||
_cache = new ConcurrentDictionary<GridCacheKey, GridCacheItem>();
|
||||
_statistics = new GridCacheStatistics { MaxCapacity = maxCapacity };
|
||||
|
||||
LogManager.Info($"[网格缓存] 初始化完成,最大容量: {_maxCapacity}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存的网格
|
||||
/// </summary>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <returns>网格对象,如果不存在则返回null</returns>
|
||||
public Grid Get(GridCacheKey key)
|
||||
{
|
||||
if (key == null)
|
||||
return null;
|
||||
|
||||
if (_cache.TryGetValue(key, out var item))
|
||||
{
|
||||
// 记录访问
|
||||
item.RecordAccess();
|
||||
_statistics.HitCount++;
|
||||
|
||||
LogManager.Debug($"[网格缓存] 命中 - {key},访问次数: {item.AccessCount}");
|
||||
return item.Grid;
|
||||
}
|
||||
|
||||
_statistics.MissCount++;
|
||||
LogManager.Debug($"[网格缓存] 未命中 - {key}");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加网格到缓存
|
||||
/// </summary>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <param name="grid">A*网格</param>
|
||||
public void Put(GridCacheKey key, Grid grid)
|
||||
{
|
||||
if (key == null || grid == null)
|
||||
return;
|
||||
|
||||
var item = new GridCacheItem(grid);
|
||||
|
||||
// 添加或更新缓存项
|
||||
_cache.AddOrUpdate(key, item, (k, existing) =>
|
||||
{
|
||||
LogManager.Debug($"[网格缓存] 更新现有项 - {key}");
|
||||
return item;
|
||||
});
|
||||
|
||||
// 更新统计信息
|
||||
_statistics.CurrentCount = _cache.Count;
|
||||
|
||||
LogManager.Debug($"[网格缓存] 添加 - {key},当前容量: {_cache.Count}/{_maxCapacity}");
|
||||
|
||||
// 检查是否需要清理
|
||||
if (_cache.Count > _maxCapacity)
|
||||
{
|
||||
CleanupCache();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查缓存中是否包含指定键
|
||||
/// </summary>
|
||||
/// <param name="key">缓存键</param>
|
||||
/// <returns>是否包含</returns>
|
||||
public bool ContainsKey(GridCacheKey key)
|
||||
{
|
||||
return key != null && _cache.ContainsKey(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有缓存
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (_cleanupLock)
|
||||
{
|
||||
var count = _cache.Count;
|
||||
_cache.Clear();
|
||||
_statistics.CurrentCount = 0;
|
||||
_statistics.Reset();
|
||||
|
||||
LogManager.Info($"[网格缓存] 清除所有缓存,共清除 {count} 项");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存的所有键
|
||||
/// </summary>
|
||||
/// <returns>键列表</returns>
|
||||
public IList<GridCacheKey> GetKeys()
|
||||
{
|
||||
return _cache.Keys.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理缓存,使用LRU策略移除最少使用的项
|
||||
/// </summary>
|
||||
private void CleanupCache()
|
||||
{
|
||||
lock (_cleanupLock)
|
||||
{
|
||||
// 双重检查,避免重复清理
|
||||
if (_cache.Count <= _maxCapacity)
|
||||
return;
|
||||
|
||||
var targetCount = (int)(_maxCapacity * 0.8); // 清理到80%容量
|
||||
var itemsToRemove = _cache.Count - targetCount;
|
||||
|
||||
LogManager.Info($"[网格缓存] 开始LRU清理,当前: {_cache.Count},目标: {targetCount},需移除: {itemsToRemove}");
|
||||
|
||||
// 按最后访问时间排序,移除最久未访问的项
|
||||
var sortedItems = _cache
|
||||
.OrderBy(kvp => kvp.Value.LastAccessTime)
|
||||
.ThenBy(kvp => kvp.Value.AccessCount) // 相同时间的情况下,优先移除访问次数少的
|
||||
.Take(itemsToRemove)
|
||||
.ToList();
|
||||
|
||||
int removedCount = 0;
|
||||
foreach (var item in sortedItems)
|
||||
{
|
||||
if (_cache.TryRemove(item.Key, out _))
|
||||
{
|
||||
removedCount++;
|
||||
_statistics.EvictedCount++;
|
||||
LogManager.Debug($"[网格缓存] 移除 - {item.Key},最后访问: {item.Value.LastAccessTime:yyyy-MM-dd HH:mm:ss}");
|
||||
}
|
||||
}
|
||||
|
||||
_statistics.CurrentCount = _cache.Count;
|
||||
LogManager.Info($"[网格缓存] LRU清理完成,实际移除: {removedCount},当前容量: {_cache.Count}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成缓存详细报告
|
||||
/// </summary>
|
||||
/// <returns>报告字符串</returns>
|
||||
public string GenerateDetailedReport()
|
||||
{
|
||||
var report = new System.Text.StringBuilder();
|
||||
|
||||
report.AppendLine(_statistics.GenerateReport());
|
||||
|
||||
if (_cache.Count > 0)
|
||||
{
|
||||
report.AppendLine($"\n当前缓存项详情:");
|
||||
var sortedItems = _cache
|
||||
.OrderByDescending(kvp => kvp.Value.LastAccessTime)
|
||||
.Take(10) // 只显示最近10项
|
||||
.ToList();
|
||||
|
||||
foreach (var item in sortedItems)
|
||||
{
|
||||
report.AppendLine($" {item.Key} - 访问{item.Value.AccessCount}次,最后访问: {item.Value.LastAccessTime:MM-dd HH:mm:ss}");
|
||||
}
|
||||
|
||||
if (_cache.Count > 10)
|
||||
{
|
||||
report.AppendLine($" ... 还有 {_cache.Count - 10} 项");
|
||||
}
|
||||
}
|
||||
|
||||
return report.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局网格缓存管理器(单例)
|
||||
/// </summary>
|
||||
public static class GlobalGridCache
|
||||
{
|
||||
private static readonly Lazy<GridCache> _instance = new Lazy<GridCache>(() => new GridCache());
|
||||
|
||||
/// <summary>
|
||||
/// 获取全局缓存实例
|
||||
/// </summary>
|
||||
public static GridCache Instance => _instance.Value;
|
||||
|
||||
/// <summary>
|
||||
/// 重置全局缓存(主要用于测试)
|
||||
/// </summary>
|
||||
public static void Reset()
|
||||
{
|
||||
_instance.Value.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,185 +0,0 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
/// <summary>
|
||||
/// 网格缓存键,用于标识唯一的网格配置
|
||||
/// 包含所有影响网格生成的因素:网格地图内容、车辆高度、路径策略、网格尺寸
|
||||
/// </summary>
|
||||
public class GridCacheKey : IEquatable<GridCacheKey>
|
||||
{
|
||||
/// <summary>
|
||||
/// 网格地图内容哈希值
|
||||
/// </summary>
|
||||
public string GridMapHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 车辆高度(模型单位)
|
||||
/// </summary>
|
||||
public double VehicleHeight { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 路径规划策略
|
||||
/// </summary>
|
||||
public PathStrategy Strategy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 网格单元大小(模型单位)
|
||||
/// </summary>
|
||||
public double CellSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="gridMapHash">网格地图哈希</param>
|
||||
/// <param name="vehicleHeight">车辆高度</param>
|
||||
/// <param name="strategy">路径策略</param>
|
||||
/// <param name="cellSize">网格尺寸</param>
|
||||
public GridCacheKey(string gridMapHash, double vehicleHeight, PathStrategy strategy, double cellSize)
|
||||
{
|
||||
GridMapHash = gridMapHash ?? throw new ArgumentNullException(nameof(gridMapHash));
|
||||
VehicleHeight = vehicleHeight;
|
||||
Strategy = strategy;
|
||||
CellSize = cellSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查两个缓存键是否相等
|
||||
/// </summary>
|
||||
public bool Equals(GridCacheKey other)
|
||||
{
|
||||
if (other == null) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
|
||||
return GridMapHash == other.GridMapHash &&
|
||||
Math.Abs(VehicleHeight - other.VehicleHeight) < 1e-6 &&
|
||||
Strategy == other.Strategy &&
|
||||
Math.Abs(CellSize - other.CellSize) < 1e-6;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写Equals方法
|
||||
/// </summary>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as GridCacheKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算哈希码,用于Dictionary键
|
||||
/// </summary>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash = 17;
|
||||
hash = hash * 23 + (GridMapHash?.GetHashCode() ?? 0);
|
||||
hash = hash * 23 + VehicleHeight.GetHashCode();
|
||||
hash = hash * 23 + Strategy.GetHashCode();
|
||||
hash = hash * 23 + CellSize.GetHashCode();
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成可读的字符串表示
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
var hashPreview = GridMapHash != null ?
|
||||
GridMapHash.Substring(0, Math.Min(8, GridMapHash.Length)) :
|
||||
"NULL";
|
||||
return $"GridCacheKey[Hash={hashPreview}, " +
|
||||
$"Height={VehicleHeight:F2}, Strategy={Strategy}, CellSize={CellSize:F2}]";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据GridMap和参数生成缓存键
|
||||
/// </summary>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <param name="vehicleHeight">车辆高度</param>
|
||||
/// <param name="strategy">路径策略</param>
|
||||
/// <returns>缓存键</returns>
|
||||
public static GridCacheKey CreateFrom(GridMap gridMap, double vehicleHeight, PathStrategy strategy)
|
||||
{
|
||||
if (gridMap == null)
|
||||
throw new ArgumentNullException(nameof(gridMap));
|
||||
|
||||
var gridMapHash = ComputeGridMapHash(gridMap);
|
||||
return new GridCacheKey(gridMapHash, vehicleHeight, strategy, gridMap.CellSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算GridMap的内容哈希值
|
||||
/// 基于网格尺寸、单元格可通行性、高度约束等关键数据
|
||||
/// </summary>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <returns>SHA256哈希字符串</returns>
|
||||
private static string ComputeGridMapHash(GridMap gridMap)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var sha256 = SHA256.Create())
|
||||
{
|
||||
var hashBuilder = new StringBuilder();
|
||||
|
||||
// 基础属性
|
||||
hashBuilder.Append($"W:{gridMap.Width}|H:{gridMap.Height}|CS:{gridMap.CellSize:F6}|");
|
||||
hashBuilder.Append($"OX:{gridMap.Origin.X:F6}|OY:{gridMap.Origin.Y:F6}|OZ:{gridMap.Origin.Z:F6}|");
|
||||
|
||||
// 遍历所有网格单元
|
||||
for (int x = 0; x < gridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height; y++)
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 基本属性
|
||||
hashBuilder.Append($"[{x},{y}]:{(cell.IsWalkable ? "1" : "0")}|{cell.CellType}|");
|
||||
|
||||
// 世界位置
|
||||
hashBuilder.Append($"WP:{cell.WorldPosition.X:F3},{cell.WorldPosition.Y:F3},{cell.WorldPosition.Z:F3}|");
|
||||
|
||||
// 高度约束区间
|
||||
if (cell.PassableHeights != null && cell.PassableHeights.Count > 0)
|
||||
{
|
||||
hashBuilder.Append("PH:");
|
||||
foreach (var interval in cell.PassableHeights)
|
||||
{
|
||||
hashBuilder.Append($"{interval.MinZ:F3}-{interval.MaxZ:F3},");
|
||||
}
|
||||
hashBuilder.Append("|");
|
||||
}
|
||||
else
|
||||
{
|
||||
hashBuilder.Append("PH:NONE|");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算哈希
|
||||
var hashInput = hashBuilder.ToString();
|
||||
var hashBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(hashInput));
|
||||
|
||||
// 转换为十六进制字符串
|
||||
var hexBuilder = new StringBuilder();
|
||||
foreach (byte b in hashBytes)
|
||||
{
|
||||
hexBuilder.Append(b.ToString("x2"));
|
||||
}
|
||||
|
||||
return hexBuilder.ToString();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 如果哈希计算失败,返回基于时间戳的唯一标识
|
||||
// 这样可以确保缓存不会错误命中,但会失去缓存效果
|
||||
LogManager.Warning($"[网格哈希] 计算网格哈希失败: {ex.Message},使用时间戳标识");
|
||||
return $"HASH_FAILED_{DateTime.Now.Ticks}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -205,10 +205,11 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <param name="gridPosition">网格坐标</param>
|
||||
/// <param name="z">Z坐标值</param>
|
||||
/// <returns>世界3D坐标点</returns>
|
||||
public Point3D GridToWorld3D(GridPoint2D gridPosition, double z)
|
||||
public Point3D GridToWorld3D(GridPoint2D gridPosition)
|
||||
{
|
||||
var world2D = GridToWorld2D(gridPosition);
|
||||
return new Point3D(world2D.X, world2D.Y, z);
|
||||
var cell = Cells[gridPosition.X, gridPosition.Y];
|
||||
return new Point3D(world2D.X, world2D.Y, cell.WorldPosition.Z);
|
||||
}
|
||||
|
||||
|
||||
@ -249,8 +250,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
if (!IsValidGridPosition(gridPosition))
|
||||
return;
|
||||
|
||||
var existingZ = Cells[gridPosition.X, gridPosition.Y].WorldPosition.Z;
|
||||
var worldPos = GridToWorld3D(gridPosition, existingZ);
|
||||
var worldPos = GridToWorld3D(gridPosition);
|
||||
var cell = new GridCell
|
||||
{
|
||||
IsWalkable = isWalkable,
|
||||
@ -277,37 +277,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
Cells[gridPosition.X, gridPosition.Y] = cell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置网格单元为通道类型 - A*改进方案新增方法
|
||||
/// </summary>
|
||||
/// <param name="gridPosition">网格坐标</param>
|
||||
/// <param name="channelType">通道类型</param>
|
||||
/// <param name="relatedItem">关联的模型物品</param>
|
||||
public void SetCellAsChannel(GridPoint2D gridPosition, ChannelType channelType = ChannelType.Corridor, ModelItem relatedItem = null, double channelHeight = 0.0)
|
||||
{
|
||||
if (!IsValidGridPosition(gridPosition))
|
||||
return;
|
||||
|
||||
// 使用传入的通道高度,如果没有提供则使用现有的Z坐标或边界最大Z
|
||||
double actualZ = channelHeight > 0 ? channelHeight :
|
||||
(Cells[gridPosition.X, gridPosition.Y].WorldPosition.Z > 0 ?
|
||||
Cells[gridPosition.X, gridPosition.Y].WorldPosition.Z :
|
||||
Bounds.Max.Z);
|
||||
|
||||
var worldPos = GridToWorld3D(gridPosition, actualZ);
|
||||
var cell = new GridCell
|
||||
{
|
||||
IsWalkable = true,
|
||||
CellType = CategoryAttributeManager.LogisticsElementType.通道,
|
||||
IsInChannel = true,
|
||||
PassableHeights = new List<HeightInterval>(),
|
||||
ChannelType = channelType,
|
||||
WorldPosition = worldPos,
|
||||
RelatedModelItem = relatedItem
|
||||
};
|
||||
cell.Cost = cell.GetCost(); // 用GetCost方法计算通道成本
|
||||
Cells[gridPosition.X, gridPosition.Y] = cell;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加可通行高度区间到指定网格单元
|
||||
|
||||
@ -125,14 +125,11 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <param name="safetyMargin">安全间隙(米)</param>
|
||||
/// <param name="vehicleHeight">车辆高度(米)</param>
|
||||
/// <returns>缓存键</returns>
|
||||
public static GridMapCacheKey CreateFrom(Document document, BoundingBox3D bounds,
|
||||
public static GridMapCacheKey CreateFrom(BoundingBox3D bounds,
|
||||
double cellSize, double vehicleRadius,
|
||||
double safetyMargin, double vehicleHeight)
|
||||
{
|
||||
if (document == null)
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
|
||||
var channelDataHash = ComputeChannelDataHash(document);
|
||||
var channelDataHash = ComputeChannelDataHash();
|
||||
var boundsHash = ComputeBoundsHash(bounds);
|
||||
|
||||
return new GridMapCacheKey(channelDataHash, boundsHash, cellSize,
|
||||
@ -143,9 +140,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// 计算通道数据哈希值
|
||||
/// 基于所有可通行物流构件的ID、属性、几何等信息
|
||||
/// </summary>
|
||||
/// <param name="document">Navisworks文档</param>
|
||||
/// <returns>通道数据哈希</returns>
|
||||
private static string ComputeChannelDataHash(Document document)
|
||||
private static string ComputeChannelDataHash()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -154,7 +150,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
var hashBuilder = new StringBuilder();
|
||||
|
||||
// 获取所有可通行的物流模型项
|
||||
var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems(document);
|
||||
var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems();
|
||||
|
||||
// 按InstanceGuid排序确保一致性
|
||||
var sortedItems = allChannelItems.OrderBy(item => item.InstanceGuid.ToString()).ToList();
|
||||
|
||||
@ -53,12 +53,12 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <param name="planningEndPoint">规划终点</param>
|
||||
/// <param name="vehicleHeight">车辆高度(米)</param>
|
||||
/// <returns>生成的网格地图</returns>
|
||||
public GridMap GenerateFromBIM(Document document, BoundingBox3D bounds, double cellSize, double vehicleRadius, double safetyMargin, Point3D planningStartPoint, Point3D planningEndPoint, double vehicleHeight)
|
||||
public GridMap GenerateFromBIM(BoundingBox3D bounds, double cellSize, double vehicleRadius, double safetyMargin, Point3D planningStartPoint, Point3D planningEndPoint, double vehicleHeight)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 生成缓存键
|
||||
var cacheKey = GridMapCacheKey.CreateFrom(document, bounds, cellSize, vehicleRadius, safetyMargin, vehicleHeight);
|
||||
var cacheKey = GridMapCacheKey.CreateFrom(bounds, cellSize, vehicleRadius, safetyMargin, vehicleHeight);
|
||||
|
||||
// 尝试从缓存获取
|
||||
var cachedGridMap = GlobalGridMapCache.Instance.Get(cacheKey, 5000); // 预估生成需要5秒
|
||||
@ -102,7 +102,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
// 1. 使用通道构建器构建基础通道覆盖网格
|
||||
LogManager.Info("【生成网格地图】步骤1: 构建通道覆盖网格");
|
||||
var channelCoverage = _channelBuilder.BuildChannelCoverage(cellSizeInModelUnits, document);
|
||||
var channelCoverage = _channelBuilder.BuildChannelCoverage(cellSizeInModelUnits);
|
||||
|
||||
LogManager.Info($"【生成网格地图】通道覆盖构建完成: {channelCoverage.GetStatistics()}");
|
||||
LogManager.Info($"【阶段1完成】通道覆盖网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
@ -111,7 +111,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
LogManager.Info("【生成网格地图】步骤1.5: 智能收集通道相关节点");
|
||||
|
||||
// 直接获取所有可通行的物流模型项
|
||||
var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems(document);
|
||||
var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems();
|
||||
LogManager.Info($"【生成网格地图】直接获取到 {allChannelItems.Count} 个可通行物流元素");
|
||||
|
||||
// 智能收集可通行模型相关节点(包括父节点、同级节点等)
|
||||
@ -120,7 +120,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
// 2. 直接遍历处理障碍物(包围盒检测) - 使用高性能优化版本
|
||||
LogManager.Info("【生成网格地图】步骤2: 高性能包围盒遍历处理障碍物");
|
||||
ProcessObstaclesWithBoundingBoxOptimized(document, channelCoverage.GridMap, channelRelatedItems, scanHeightInModelUnits);
|
||||
ProcessObstaclesWithBoundingBoxOptimized(channelCoverage.GridMap, channelRelatedItems, scanHeightInModelUnits);
|
||||
|
||||
LogManager.Info($"【阶段2完成】障碍物处理后网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
|
||||
@ -292,8 +292,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 更新门网格的Z坐标为门底部位置
|
||||
var worldPos = gridMap.GridToWorld3D(gridPos, doorBottomZ);
|
||||
cell.WorldPosition = worldPos;
|
||||
var world2D = gridMap.GridToWorld2D(gridPos);
|
||||
cell.WorldPosition = new Point3D(world2D.X, world2D.Y, doorBottomZ);
|
||||
|
||||
// 将门的高度范围存储在PassableHeights中供后续使用(使用相对坐标)
|
||||
if (doorHeight > 0)
|
||||
@ -407,7 +407,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
if (cell.CellType == CategoryAttributeManager.LogisticsElementType.通道 && cell.IsInChannel)
|
||||
{
|
||||
// 使用网格的实际Z坐标
|
||||
var worldPos = gridMap.GridToWorld3D(new GridPoint2D(x, y), cell.WorldPosition.Z);
|
||||
var worldPos = gridMap.GridToWorld3D(new GridPoint2D(x, y));
|
||||
|
||||
// 重要修复:使用通道顶面作为垂直扫描的起点,而不是底面
|
||||
// 从通道构建器的日志可知通道总边界MaxZ = 38.8,这是正确的扫描起点
|
||||
@ -942,7 +942,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// 使用Search API一次性获取所有有几何体的模型项
|
||||
/// 这比逐项调用HasGeometry快得多
|
||||
/// </summary>
|
||||
private List<ModelItem> GetGeometryItemsUsingSearchAPI(Document document)
|
||||
private List<ModelItem> GetGeometryItemsUsingSearchAPI()
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -950,7 +950,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
var startTime = DateTime.Now;
|
||||
|
||||
// 统计模型总数
|
||||
var totalModelItems = document.Models.RootItemDescendantsAndSelf.Count();
|
||||
var totalModelItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf.Count();
|
||||
LogManager.Info($"[Search API优化] 模型总数统计: {totalModelItems} 个模型项");
|
||||
|
||||
var search = new Search();
|
||||
@ -963,7 +963,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
search.Locations = SearchLocations.DescendantsAndSelf;
|
||||
|
||||
// 一次性获取所有有几何体的项目
|
||||
var geometryItems = search.FindAll(document, false);
|
||||
var geometryItems = search.FindAll(Application.ActiveDocument, false);
|
||||
|
||||
var searchElapsed = (DateTime.Now - startTime).TotalMilliseconds;
|
||||
var filteredByGeometry = totalModelItems - geometryItems.Count();
|
||||
@ -1146,7 +1146,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <param name="channelItems">通道模型项列表</param>
|
||||
/// <param name="scanHeightInModelUnits">扫描高度范围(模型单位)</param>
|
||||
private void ProcessObstaclesWithBoundingBoxOptimized(Document document, GridMap gridMap, HashSet<ModelItem> channelItems, double scanHeightInModelUnits)
|
||||
private void ProcessObstaclesWithBoundingBoxOptimized(GridMap gridMap, HashSet<ModelItem> channelItems, double scanHeightInModelUnits)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -1156,7 +1156,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
var channelItemsSet = channelItems ?? new HashSet<ModelItem>();
|
||||
|
||||
// 阶段1:Search API预筛选(一次性批量获取几何体项目)
|
||||
var geometryItems = GetGeometryItemsUsingSearchAPI(document);
|
||||
var geometryItems = GetGeometryItemsUsingSearchAPI();
|
||||
|
||||
LogManager.Info($"[高性能障碍物处理] 输入统计 - 几何体项目: {geometryItems.Count}, 通道元素: {channelItemsSet.Count}");
|
||||
|
||||
|
||||
@ -842,7 +842,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
return null;
|
||||
|
||||
// 直接提取模型项的三角形几何数据
|
||||
var triangles = GeometryHelper.ExtractTrianglesOptimized(item);
|
||||
var triangles = GeometryHelper.ExtractTriangles(item);
|
||||
if (triangles == null || triangles.Count == 0)
|
||||
{
|
||||
return null;
|
||||
|
||||
@ -716,7 +716,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
// 直接使用 CategoryAttributeManager 的 API
|
||||
var document = NavisApplication.ActiveDocument;
|
||||
var logisticsItems = CategoryAttributeManager.GetAllLogisticsItems(document);
|
||||
var logisticsItems = CategoryAttributeManager.GetAllLogisticsItems();
|
||||
var models = new List<LogisticsModel>();
|
||||
|
||||
foreach (var item in logisticsItems)
|
||||
@ -1121,7 +1121,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
var logisticsItems = CategoryAttributeManager.GetAllLogisticsItems(document);
|
||||
var logisticsItems = CategoryAttributeManager.GetAllLogisticsItems();
|
||||
|
||||
if (logisticsItems == null || logisticsItems.Count == 0)
|
||||
{
|
||||
|
||||
@ -100,7 +100,7 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
// 使用优化的几何提取方法
|
||||
var triangles = ExtractTrianglesOptimized(targetItem);
|
||||
var triangles = ExtractTriangles(targetItem);
|
||||
LogManager.WriteLog($"提取到 {triangles.Count} 个三角形");
|
||||
|
||||
if (triangles.Count > 0)
|
||||
@ -124,11 +124,11 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 优化的三角形提取方法
|
||||
/// 提取三角形
|
||||
/// </summary>
|
||||
/// <param name="modelItem">模型项</param>
|
||||
/// <returns>三角形集合</returns>
|
||||
public static List<Triangle3D> ExtractTrianglesOptimized(ModelItem modelItem)
|
||||
public static List<Triangle3D> ExtractTriangles(ModelItem modelItem)
|
||||
{
|
||||
var triangles = new List<Triangle3D>();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user