通道网格用几何三角形精确计算,增加通行网格可视化
This commit is contained in:
parent
2955bfd38b
commit
e73cd2113e
@ -245,7 +245,7 @@
|
||||
<Compile Include="src\Utils\CoordinateConverter.cs" />
|
||||
<Compile Include="src\Utils\FloorDetector.cs" />
|
||||
<Compile Include="src\Utils\ModelItemAnalysisHelper.cs" />
|
||||
<Compile Include="src\Utils\GeometryExtractor.cs" />
|
||||
<Compile Include="src\Utils\GeometryHelper.cs" />
|
||||
<Compile Include="src\Utils\LogManager.cs" />
|
||||
<Compile Include="src\Utils\NavisworksApiHelper.cs" />
|
||||
<Compile Include="src\Utils\NavisworksSelectionHelper.cs" />
|
||||
|
||||
@ -1309,7 +1309,7 @@ public class CustomPropertyManager
|
||||
|
||||
**日志验证**:
|
||||
|
||||
```
|
||||
```text
|
||||
[FloorAttributeManager] 找到楼层属性分类,索引为: 0
|
||||
[FloorAttributeManager] ✅ 成功更新现有楼层属性分类 (index=1)
|
||||
```
|
||||
|
||||
@ -582,16 +582,16 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除3D路径标记
|
||||
/// 清除3D路径标记(保留网格可视化)
|
||||
/// </summary>
|
||||
public void Clear3DPathMarkers()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pathPointMarkers?.Clear();
|
||||
// 通知渲染插件更新显示
|
||||
_renderPlugin?.ClearAllPaths();
|
||||
LogManager.Info("3D路径标记已清除");
|
||||
// 清除路径标记但保留网格可视化
|
||||
_renderPlugin?.ClearPathsExcept("grid_visualization_all");
|
||||
LogManager.Info("3D路径标记已清除(保留网格可视化)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -854,6 +854,10 @@ namespace NavisworksTransport
|
||||
|
||||
LogManager.Info($"网格地图生成完成: {gridMap.GetStatistics()}");
|
||||
|
||||
// 可视化网格(调试用)
|
||||
LogManager.Info("开始网格可视化,显示所有可通行网格单元");
|
||||
VisualizeGridCells(gridMap);
|
||||
|
||||
// 3. 获取通道数据用于精确高度计算
|
||||
LogManager.Info("开始获取通道数据用于精确高度计算...");
|
||||
var channelItems = GetChannelItemsForHeightCalculation(gridMap);
|
||||
@ -3065,6 +3069,147 @@ namespace NavisworksTransport
|
||||
|
||||
#endregion
|
||||
|
||||
#region 网格可视化方法
|
||||
|
||||
/// <summary>
|
||||
/// 在3D视图中可视化网格中的可通行单元格
|
||||
/// 在每个可通行网格的中心绘制一个绿色小球
|
||||
/// </summary>
|
||||
/// <param name="gridMap">要可视化的网格地图</param>
|
||||
public void VisualizeGridCells(GridMap gridMap)
|
||||
{
|
||||
if (gridMap == null)
|
||||
{
|
||||
LogManager.Warning("[网格可视化] 网格地图为null,无法进行可视化");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
LogManager.Info($"[网格可视化] 开始可视化网格:{gridMap.Width}x{gridMap.Height}");
|
||||
|
||||
// 获取渲染插件
|
||||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||||
if (renderPlugin == null)
|
||||
{
|
||||
LogManager.Warning("[网格可视化] PathPointRenderPlugin实例为空,无法绘制网格");
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除之前的网格可视化
|
||||
ClearGridVisualization();
|
||||
|
||||
// 创建单一的网格可视化路径
|
||||
var gridVisualizationRoute = new PathRoute("GridVisualization")
|
||||
{
|
||||
Id = "grid_visualization_all"
|
||||
};
|
||||
|
||||
// 统计变量
|
||||
int walkableCells = 0;
|
||||
int channelCells = 0;
|
||||
int openSpaceCells = 0;
|
||||
int visualizedCells = 0;
|
||||
const int maxVisualizationPoints = 5000; // 限制最大可视化点数
|
||||
|
||||
// 遍历所有网格单元格,批量收集可通行网格点
|
||||
for (int x = 0; x < gridMap.Width && visualizedCells < maxVisualizationPoints; x++)
|
||||
{
|
||||
for (int y = 0; y < gridMap.Height && visualizedCells < maxVisualizationPoints; y++)
|
||||
{
|
||||
var cell = gridMap.Cells[x, y];
|
||||
|
||||
// 只处理可通行的单元格
|
||||
if (cell.IsWalkable)
|
||||
{
|
||||
walkableCells++;
|
||||
|
||||
// 根据单元格类型分类统计
|
||||
if (cell.CellType == ElementType.Channel)
|
||||
channelCells++;
|
||||
else if (cell.CellType == ElementType.OpenSpace)
|
||||
openSpaceCells++;
|
||||
|
||||
// 计算网格单元格的世界中心位置
|
||||
var gridPos = new NavisworksTransport.PathPlanning.Point2D(x, y);
|
||||
var worldCenter = gridMap.GridToWorld(gridPos);
|
||||
|
||||
// 使用网格单元格中心的Z坐标,如果没有则使用网格边界的最大Z值
|
||||
double centerZ = cell.WorldPosition.Z != 0 ? cell.WorldPosition.Z : gridMap.Bounds.Max.Z;
|
||||
var visualizationPoint = new Point3D(worldCenter.X, worldCenter.Y, centerZ);
|
||||
|
||||
// 创建网格可视化点
|
||||
var gridPoint = new PathPoint
|
||||
{
|
||||
Position = visualizationPoint,
|
||||
Name = $"网格({x},{y})",
|
||||
Type = PathPointType.WayPoint,
|
||||
Index = visualizedCells // 设置索引
|
||||
};
|
||||
|
||||
// 添加到统一的路径中
|
||||
gridVisualizationRoute.Points.Add(gridPoint);
|
||||
visualizedCells++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 批量渲染所有网格点
|
||||
if (gridVisualizationRoute.Points.Count > 0)
|
||||
{
|
||||
renderPlugin.RenderPointOnly(gridVisualizationRoute);
|
||||
}
|
||||
|
||||
// 输出统计信息
|
||||
LogManager.Info($"[网格可视化] 可视化完成:");
|
||||
LogManager.Info($" - 总网格单元格数:{gridMap.Width * gridMap.Height}");
|
||||
LogManager.Info($" - 可通行单元格数:{walkableCells}");
|
||||
LogManager.Info($" - 其中通道单元格:{channelCells}");
|
||||
LogManager.Info($" - 其中开放空间单元格:{openSpaceCells}");
|
||||
LogManager.Info($" - 已可视化单元格数:{visualizedCells}");
|
||||
if (visualizedCells >= maxVisualizationPoints)
|
||||
{
|
||||
LogManager.Info($" - 注意:可视化点数已达到上限 {maxVisualizationPoints}");
|
||||
}
|
||||
|
||||
RaiseStatusChanged($"网格可视化完成:显示了 {visualizedCells} 个可通行网格单元", PathPlanningStatusType.Success);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[网格可视化] 可视化失败: {ex.Message}");
|
||||
RaiseErrorOccurred($"网格可视化失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除网格可视化
|
||||
/// </summary>
|
||||
public void ClearGridVisualization()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info("[网格可视化] 开始清除网格可视化");
|
||||
|
||||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||||
if (renderPlugin != null)
|
||||
{
|
||||
// 只清除网格可视化路径
|
||||
renderPlugin.RemovePath("grid_visualization_all");
|
||||
LogManager.Info("[网格可视化] 网格可视化已清除");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning("[网格可视化] PathPointRenderPlugin实例为空,无法清除可视化");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[网格可视化] 清除可视化失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 静态方法
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -305,6 +305,9 @@ namespace NavisworksTransport
|
||||
|
||||
try
|
||||
{
|
||||
// 检查是否是网格可视化路径
|
||||
bool isGridVisualization = pathRoute.Id == "grid_visualization_all";
|
||||
|
||||
var visualization = new PathVisualization
|
||||
{
|
||||
PathId = pathRoute.Id,
|
||||
@ -319,7 +322,16 @@ namespace NavisworksTransport
|
||||
_pathVisualizations[pathRoute.Id] = visualization;
|
||||
}
|
||||
|
||||
LogManager.WriteLog($"[点标记渲染] 渲染点标记: {pathRoute.Name}, 点数: {pathRoute.Points.Count}");
|
||||
// 网格可视化只打印统计日志,不打印详细日志
|
||||
if (!isGridVisualization)
|
||||
{
|
||||
LogManager.WriteLog($"[点标记渲染] 渲染点标记: {pathRoute.Name}, 点数: {pathRoute.Points.Count}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.WriteLog($"[网格可视化] 渲染网格点: {pathRoute.Points.Count} 个可通行单元");
|
||||
}
|
||||
|
||||
RequestViewRefresh();
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -340,6 +352,9 @@ namespace NavisworksTransport
|
||||
var points = visualization.PathRoute.Points;
|
||||
if (points.Count == 0) return;
|
||||
|
||||
// 检查是否是网格可视化路径
|
||||
bool isGridVisualization = visualization.PathId == "grid_visualization_all";
|
||||
|
||||
// 按索引排序点
|
||||
var sortedPoints = points.OrderBy(p => p.Index).ToList();
|
||||
|
||||
@ -351,7 +366,12 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
visualization.LastUpdated = DateTime.Now;
|
||||
LogManager.WriteLog($"[点标记构建] 已构建 {visualization.PointMarkers.Count} 个点标记,无连线");
|
||||
|
||||
// 网格可视化不打印详细构建日志,避免日志泛滥
|
||||
if (!isGridVisualization)
|
||||
{
|
||||
LogManager.WriteLog($"[点标记构建] 已构建 {visualization.PointMarkers.Count} 个点标记,无连线");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -401,6 +421,34 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空路径,但保留指定的路径
|
||||
/// </summary>
|
||||
/// <param name="excludedPathIds">要保留的路径ID列表</param>
|
||||
public void ClearPathsExcept(params string[] excludedPathIds)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
var excludedSet = new HashSet<string>(excludedPathIds);
|
||||
var toRemove = _pathVisualizations.Keys.Where(id => !excludedSet.Contains(id)).ToList();
|
||||
|
||||
int removedCount = 0;
|
||||
foreach (var pathId in toRemove)
|
||||
{
|
||||
if (_pathVisualizations.Remove(pathId))
|
||||
{
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.WriteLog($"[选择性清空] 清空{removedCount}个路径,保留{excludedSet.Count}个路径");
|
||||
if (removedCount > 0)
|
||||
{
|
||||
RequestViewRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建路径可视化
|
||||
/// </summary>
|
||||
@ -456,12 +504,15 @@ namespace NavisworksTransport
|
||||
/// <returns>圆形标记</returns>
|
||||
private CircleMarker CreatePointMarker(PathPoint point)
|
||||
{
|
||||
// 检查是否是网格可视化点(通过名称判断)
|
||||
bool isGridVisualization = point.Name.StartsWith("网格(");
|
||||
|
||||
return new CircleMarker
|
||||
{
|
||||
Center = point.Position,
|
||||
Normal = new Vector3D(0, 0, 1),
|
||||
Radius = GetRadiusForPointType(point.Type),
|
||||
Color = GetColorForPointType(point.Type),
|
||||
Radius = isGridVisualization ? GetRadiusForGridVisualization() : GetRadiusForPointType(point.Type),
|
||||
Color = isGridVisualization ? Color.Green : GetColorForPointType(point.Type),
|
||||
Alpha = 1.0,
|
||||
Filled = true,
|
||||
PointType = point.Type,
|
||||
@ -470,6 +521,18 @@ namespace NavisworksTransport
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取网格可视化小球的半径(比正常路径点小)
|
||||
/// </summary>
|
||||
/// <returns>网格可视化半径</returns>
|
||||
private double GetRadiusForGridVisualization()
|
||||
{
|
||||
// 网格可视化小球使用0.2米的物理尺寸,比正常路径点小
|
||||
double baseRadiusInMeters = 0.2;
|
||||
double metersToModelUnits = GetMetersToModelUnitsConversionFactor();
|
||||
return baseRadiusInMeters * metersToModelUnits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取连线颜色(统一使用橙色)
|
||||
/// </summary>
|
||||
|
||||
@ -281,6 +281,56 @@ namespace NavisworksTransport
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的物流项目
|
||||
/// </summary>
|
||||
/// <param name="elementType">物流元素类型</param>
|
||||
/// <param name="document">文档,如果为null则使用ActiveDocument</param>
|
||||
/// <returns>指定类型的物流项目列表</returns>
|
||||
public static List<ModelItem> GetLogisticsItemsByType(LogisticsElementType elementType, Document document = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 如果没有提供文档,使用ActiveDocument
|
||||
if (document == null)
|
||||
document = Application.ActiveDocument;
|
||||
|
||||
// 检查文档有效性
|
||||
if (document?.Models == null)
|
||||
{
|
||||
LogManager.Warning($"[CategoryAttributeManager] 无法获取文档模型");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
|
||||
// 直接使用 Search API 搜索整个文档
|
||||
using (var search = new Search())
|
||||
{
|
||||
// 搜索整个文档的根项及其所有后代
|
||||
search.Selection.SelectAll();
|
||||
search.Locations = SearchLocations.DescendantsAndSelf;
|
||||
|
||||
var searchConditions = search.SearchConditions;
|
||||
searchConditions.Clear();
|
||||
|
||||
// 搜索条件:必须有物流分类
|
||||
searchConditions.Add(SearchCondition.HasCategoryByDisplayName(LogisticsCategories.LOGISTICS));
|
||||
|
||||
// 搜索条件:类型属性等于指定值
|
||||
searchConditions.Add(
|
||||
SearchCondition.HasPropertyByDisplayName(LogisticsCategories.LOGISTICS, LogisticsProperties.TYPE)
|
||||
.EqualValue(VariantData.FromDisplayString(elementType.ToString())));
|
||||
|
||||
// 执行搜索并返回列表
|
||||
return search.FindAll(document, false).ToList();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[CategoryAttributeManager] 获取物流项目失败: {ex.Message}");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据物流属性筛选模型项
|
||||
/// </summary>
|
||||
|
||||
@ -18,9 +18,29 @@ namespace NavisworksTransport.PathPlanning
|
||||
/// </summary>
|
||||
public class ChannelBasedGridBuilder
|
||||
{
|
||||
private CategoryAttributeManager _categoryManager;
|
||||
private ChannelHeightDetector _heightDetector;
|
||||
|
||||
/// <summary>
|
||||
/// 通道几何类型
|
||||
/// </summary>
|
||||
private enum ChannelGeometryType
|
||||
{
|
||||
/// <summary>
|
||||
/// 水平通道(楼板)
|
||||
/// </summary>
|
||||
Horizontal,
|
||||
|
||||
/// <summary>
|
||||
/// 倾斜通道(楼梯、坡道)
|
||||
/// </summary>
|
||||
Inclined,
|
||||
|
||||
/// <summary>
|
||||
/// 复杂形状
|
||||
/// </summary>
|
||||
Complex
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
@ -44,8 +64,8 @@ namespace NavisworksTransport.PathPlanning
|
||||
LogManager.Info("[通道网格构建器] 开始构建通道覆盖网格");
|
||||
|
||||
// 1. 获取所有通道物品
|
||||
var allItems = document.Models.RootItemDescendantsAndSelf;
|
||||
var channelItems = GetChannelItems(allItems.ToList());
|
||||
var channelItems = CategoryAttributeManager.GetLogisticsItemsByType(
|
||||
CategoryAttributeManager.LogisticsElementType.通道, document);
|
||||
|
||||
if (!channelItems.Any())
|
||||
{
|
||||
@ -87,177 +107,70 @@ namespace NavisworksTransport.PathPlanning
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有通道物品
|
||||
/// </summary>
|
||||
/// <param name="allItems">所有模型物品</param>
|
||||
/// <returns>通道物品列表</returns>
|
||||
private List<ModelItem> GetChannelItems(List<ModelItem> allItems)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 初始化CategoryAttributeManager(如果尚未初始化)
|
||||
if (_categoryManager == null)
|
||||
{
|
||||
_categoryManager = new CategoryAttributeManager();
|
||||
}
|
||||
|
||||
// 使用CategoryAttributeManager获取标记为"通道"的物品
|
||||
var document = Application.ActiveDocument;
|
||||
if (document?.Models != null)
|
||||
{
|
||||
var allItemsCollection = new ModelItemCollection();
|
||||
foreach (ModelItem item in document.Models.RootItemDescendantsAndSelf)
|
||||
{
|
||||
allItemsCollection.Add(item);
|
||||
}
|
||||
var channelItems = CategoryAttributeManager.FilterByLogisticsType(allItemsCollection, CategoryAttributeManager.LogisticsElementType.通道).ToList();
|
||||
|
||||
if (channelItems.Any())
|
||||
{
|
||||
LogManager.Info($"[通道网格构建器] 通过CategoryAttributeManager找到 {channelItems.Count} 个通道物品");
|
||||
return channelItems;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有用户标记的通道,直接返回空列表
|
||||
LogManager.Warning("[通道网格构建器] 未找到用户标记的通道物品,返回空列表");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[通道网格构建器] 获取通道物品时发生错误: {ex.Message}");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将通道投影到网格
|
||||
/// 核心创新:智能投影策略 - 薄通道优先使用顶面提取,复杂通道使用射线扫描
|
||||
/// 统一投影方式:基于三角形几何的精确投影,解决楼板洞口和不规则形状问题
|
||||
/// </summary>
|
||||
/// <param name="channel">通道物品</param>
|
||||
/// <param name="gridMap">目标网格地图</param>
|
||||
private void ProjectChannelToGrid(ModelItem channel, GridMap gridMap)
|
||||
{
|
||||
var bbox = channel.BoundingBox();
|
||||
var thickness = bbox.Max.Z - bbox.Min.Z;
|
||||
LogManager.Info($"[通道网格构建器] 投影通道: {channel.DisplayName}");
|
||||
|
||||
LogManager.Info($"[通道网格构建器] 投影通道: {channel.DisplayName}, 厚度: {thickness:F2}m");
|
||||
|
||||
// 智能投影策略选择
|
||||
if (thickness < 1.5) // 薄通道优先使用顶面提取
|
||||
{
|
||||
var triangles = GeometryExtractor.ExtractTrianglesOptimized(channel);
|
||||
var topFaceCount = CountTopFaces(triangles);
|
||||
var topFaceRatio = triangles.Count > 0 ? (double)topFaceCount / triangles.Count : 0;
|
||||
|
||||
// 如果有足够的水平顶面(>10%),使用顶面投影
|
||||
if (topFaceRatio > 0.1)
|
||||
{
|
||||
ProjectUsingTopFaces(channel, gridMap, triangles);
|
||||
LogManager.Info($"[通道网格构建器] 使用顶面提取投影(顶面比例: {topFaceRatio:P1})");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 对于复杂形状或没有明显顶面的通道,使用射线扫描
|
||||
ProjectUsingRayCasting(channel, gridMap);
|
||||
LogManager.Info($"[通道网格构建器] 使用垂直射线扫描(精确投影)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 方法1:顶面提取投影(快速且精确)
|
||||
/// </summary>
|
||||
/// <param name="item">模型物品</param>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
/// <param name="triangles">三角形列表(可选,避免重复提取)</param>
|
||||
private void ProjectUsingTopFaces(ModelItem item, GridMap gridMap, List<Triangle3D> triangles = null)
|
||||
{
|
||||
if (triangles == null)
|
||||
{
|
||||
triangles = GeometryExtractor.ExtractTrianglesOptimized(item);
|
||||
}
|
||||
|
||||
int projectedTriangles = 0;
|
||||
foreach (var triangle in triangles)
|
||||
{
|
||||
// 计算法向量
|
||||
var normal = ComputeTriangleNormal(triangle);
|
||||
|
||||
// 只投影朝上的面(法向量Z分量 > 0.7,约45度)
|
||||
if (normal.Z > 0.7)
|
||||
{
|
||||
// 光栅化三角形到网格
|
||||
RasterizeTriangleToGrid(gridMap, triangle);
|
||||
projectedTriangles++;
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[通道网格构建器] 顶面投影:{projectedTriangles}/{triangles.Count} 个三角形");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 方法2:垂直射线扫描(最精确)
|
||||
/// </summary>
|
||||
/// <param name="item">模型物品</param>
|
||||
/// <param name="gridMap">网格地图</param>
|
||||
private void ProjectUsingRayCasting(ModelItem item, GridMap gridMap)
|
||||
{
|
||||
var triangles = GeometryExtractor.ExtractTrianglesOptimized(item);
|
||||
// 提取三角形几何
|
||||
var triangles = GeometryHelper.ExtractTrianglesOptimized(channel);
|
||||
if (triangles.Count == 0)
|
||||
{
|
||||
LogManager.Warning($"[通道网格构建器] 未能从 '{item.DisplayName}' 提取到三角形");
|
||||
LogManager.Warning($"[通道网格构建器] 未能从 '{channel.DisplayName}' 提取到三角形");
|
||||
return;
|
||||
}
|
||||
|
||||
var bbox = item.BoundingBox();
|
||||
var minGrid = gridMap.WorldToGrid(new Point3D(bbox.Min.X, bbox.Min.Y, bbox.Min.Z));
|
||||
var maxGrid = gridMap.WorldToGrid(new Point3D(bbox.Max.X, bbox.Max.Y, bbox.Max.Z));
|
||||
|
||||
int hitCount = 0;
|
||||
|
||||
// 并行扫描网格点
|
||||
var gridPoints = new List<Point2D>();
|
||||
for (int x = minGrid.X; x <= maxGrid.X; x++)
|
||||
// 分析通道类型
|
||||
var channelType = AnalyzeChannelType(triangles);
|
||||
|
||||
// 统计三角形方向分布(用于调试)
|
||||
int upFaces = 0, inclinedFaces = 0, downFaces = 0, sideFaces = 0;
|
||||
foreach (var triangle in triangles)
|
||||
{
|
||||
for (int y = minGrid.Y; y <= maxGrid.Y; y++)
|
||||
{
|
||||
if (gridMap.IsValidGridPosition(new Point2D(x, y)))
|
||||
{
|
||||
gridPoints.Add(new Point2D(x, y));
|
||||
}
|
||||
}
|
||||
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++;
|
||||
}
|
||||
|
||||
foreach (var gridPos in gridPoints)
|
||||
|
||||
LogManager.Info($"[通道网格构建器] 通道类型: {channelType}");
|
||||
LogManager.Info($"[通道网格构建器] 三角形分布: 朝上={upFaces}, 朝上斜面={inclinedFaces}, 朝下={downFaces}, 侧面={sideFaces}, 总计={triangles.Count}");
|
||||
|
||||
// 统一投影处理
|
||||
int processedTriangles = 0;
|
||||
foreach (var triangle in triangles)
|
||||
{
|
||||
var worldPos = gridMap.GridToWorld(gridPos);
|
||||
var rayOrigin = new Point3D(worldPos.X, worldPos.Y, bbox.Max.Z + 1);
|
||||
var rayDirection = new Point3D(0, 0, -1);
|
||||
|
||||
// 检查射线是否击中任何三角形
|
||||
foreach (var triangle in triangles)
|
||||
var normal = ComputeTriangleNormal(triangle);
|
||||
|
||||
// 根据法向量决定处理方式
|
||||
if (normal.Z > 0.7) // 朝上的水平面
|
||||
{
|
||||
if (GeometryExtractor.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
|
||||
{
|
||||
if (intersectionZ >= bbox.Min.Z && intersectionZ <= bbox.Max.Z)
|
||||
{
|
||||
SetCellAsChannel(gridMap, gridPos, item);
|
||||
hitCount++;
|
||||
break; // 找到一个交点即可
|
||||
}
|
||||
}
|
||||
RasterizeTriangleToGrid(gridMap, triangle);
|
||||
processedTriangles++;
|
||||
}
|
||||
else if (normal.Z > 0.2) // 可行走的斜面(楼梯、坡道)
|
||||
{
|
||||
// 对于斜面,也进行投影,适用于楼梯等倾斜通道
|
||||
RasterizeTriangleToGrid(gridMap, triangle);
|
||||
processedTriangles++;
|
||||
}
|
||||
// 垂直面和朝下的面不投影
|
||||
}
|
||||
|
||||
LogManager.Info($"[通道网格构建器] 射线投影:{hitCount} 个网格单元");
|
||||
|
||||
LogManager.Info($"[通道网格构建器] 投影完成:{processedTriangles}/{triangles.Count} 个三角形");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 计算三角形法向量
|
||||
/// </summary>
|
||||
@ -291,24 +204,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
return normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 统计顶面数量
|
||||
/// </summary>
|
||||
/// <param name="triangles">三角形列表</param>
|
||||
/// <returns>顶面数量</returns>
|
||||
private int CountTopFaces(List<Triangle3D> triangles)
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var triangle in triangles)
|
||||
{
|
||||
var normal = ComputeTriangleNormal(triangle);
|
||||
if (normal.Z > 0.7) // 朝上的面
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将三角形光栅化到网格
|
||||
@ -337,7 +232,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
var worldPos = gridMap.GridToWorld(gridPos);
|
||||
|
||||
// 检查点是否在三角形内部
|
||||
if (GeometryExtractor.IsPointInTriangle2D(worldPos, triangle))
|
||||
if (GeometryHelper.IsPointInTriangle2D(worldPos, triangle))
|
||||
{
|
||||
SetCellAsChannel(gridMap, gridPos, null);
|
||||
}
|
||||
@ -375,6 +270,50 @@ 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>
|
||||
|
||||
@ -662,7 +662,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
LogManager.Info($"[COM几何射线] 开始从ModelItem提取三角形几何数据: {channel.DisplayName}");
|
||||
|
||||
// 使用COM API提取三角形几何数据
|
||||
var triangles = GeometryExtractor.ExtractTrianglesOptimized(channel);
|
||||
var triangles = GeometryHelper.ExtractTrianglesOptimized(channel);
|
||||
if (triangles.Count == 0)
|
||||
{
|
||||
LogManager.Warning($"[COM几何射线] 未能从ModelItem提取到三角形数据: {channel.DisplayName}");
|
||||
@ -714,7 +714,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
int intersectionCount = 0;
|
||||
foreach (var triangle in triangles)
|
||||
{
|
||||
if (GeometryExtractor.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
|
||||
if (GeometryHelper.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
|
||||
{
|
||||
intersectionPoints.Add(intersectionZ);
|
||||
intersectionCount++;
|
||||
|
||||
@ -238,25 +238,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
double scanHeight = vehicleHeight + safetyMargin; // 扫描高度 = 车辆高度 + 安全间隙
|
||||
ProcessObstaclesWithBoundingBox(document, channelCoverage.GridMap, channelRelatedItems, scanHeight);
|
||||
|
||||
// 步骤2.5: 将剩余的通道网格设置为可通行
|
||||
LogManager.Info("[包围盒2.5D模式] 步骤2.5: 设置通道网格为可通行");
|
||||
int walkableCount = 0;
|
||||
for (int x = 0; x < channelCoverage.GridMap.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < channelCoverage.GridMap.Height; y++)
|
||||
{
|
||||
var cell = channelCoverage.GridMap.Cells[x, y];
|
||||
if (cell.CellType == ElementType.Channel && cell.IsInChannel)
|
||||
{
|
||||
cell.IsWalkable = true;
|
||||
cell.Cost = 1.0;
|
||||
channelCoverage.GridMap.Cells[x, y] = cell;
|
||||
walkableCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
LogManager.Info($"[包围盒2.5D模式] 设置了 {walkableCount} 个通道网格为可通行");
|
||||
|
||||
LogManager.Info($"[阶段2完成] 障碍物处理后网格统计: {channelCoverage.GridMap.GetStatistics()}");
|
||||
|
||||
// 3. 应用车辆尺寸膨胀(如果需要)
|
||||
@ -1222,13 +1203,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
if (updatedCells > 0)
|
||||
{
|
||||
Interlocked.Increment(ref obstacleItems);
|
||||
|
||||
// 添加调试日志
|
||||
//LogManager.Info($"[包围盒障碍物处理] 障碍物 #{obstacleItems}: {item.DisplayName ?? "未命名"}, 类型:{item.ClassDisplayName}");
|
||||
//LogManager.Info($" 覆盖网格数: {updatedCells}");
|
||||
//LogManager.Info($" 包围盒: Min({bbox.Min.X:F2},{bbox.Min.Y:F2},{bbox.Min.Z:F2}) Max({bbox.Max.X:F2},{bbox.Max.Y:F2},{bbox.Max.Z:F2})");
|
||||
//LogManager.Info($" 包围盒尺寸: 宽{bbox.Max.X - bbox.Min.X:F2} x 长{bbox.Max.Y - bbox.Min.Y:F2} x 高{bbox.Max.Z - bbox.Min.Z:F2}");
|
||||
Interlocked.Increment(ref obstacleItems);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@ -847,7 +847,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
return null;
|
||||
|
||||
// 直接提取模型项的三角形几何数据
|
||||
var triangles = GeometryExtractor.ExtractTrianglesOptimized(item);
|
||||
var triangles = GeometryHelper.ExtractTrianglesOptimized(item);
|
||||
if (triangles == null || triangles.Count == 0)
|
||||
{
|
||||
return null;
|
||||
@ -909,7 +909,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
foreach (var triangle in triangles)
|
||||
{
|
||||
if (GeometryExtractor.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
|
||||
if (GeometryHelper.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
|
||||
{
|
||||
// 检查相交点是否在扫描范围内
|
||||
if (intersectionZ >= basePoint.Z && intersectionZ <= basePoint.Z + scanHeight)
|
||||
|
||||
@ -152,9 +152,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 清理所有现有的路径可视化
|
||||
PathPointRenderPlugin.Instance.ClearAllPaths();
|
||||
LogManager.WriteLog("[路径可视化] 已清理所有现有路径显示");
|
||||
// 1. 清理现有的路径可视化,但保留网格可视化
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all");
|
||||
LogManager.WriteLog("[路径可视化] 已清理现有路径显示(保留网格可视化)");
|
||||
|
||||
// 2. 如果有选中的路径,则显示该路径
|
||||
if (_selectedPathRoute != null && _selectedPathRoute.Points.Count > 0)
|
||||
@ -465,8 +465,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
PathPointRenderPlugin.Instance.ClearAllPaths();
|
||||
LogManager.Info("新建路径:已清除所有现有路径可视化显示");
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all");
|
||||
LogManager.Info("新建路径:已清除现有路径可视化显示(保留网格可视化)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -733,8 +733,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
ClearTemporaryAutoPathMarkers();
|
||||
if (PathPointRenderPlugin.Instance != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.ClearAllPaths(); // 清理所有可能的历史路径
|
||||
LogManager.WriteLog("[自动路径规划] 已清理所有历史路径对象");
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all"); // 清理历史路径但保留网格可视化
|
||||
LogManager.WriteLog("[自动路径规划] 已清理历史路径对象(保留网格可视化)");
|
||||
}
|
||||
|
||||
// 调用PathPlanningManager的自动路径规划功能
|
||||
@ -821,8 +821,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 确保失败后也清理所有可能的残留路径对象
|
||||
if (PathPointRenderPlugin.Instance != null)
|
||||
{
|
||||
PathPointRenderPlugin.Instance.ClearAllPaths();
|
||||
LogManager.WriteLog("[自动路径规划] 规划失败后已清理所有渲染对象");
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all");
|
||||
LogManager.WriteLog("[自动路径规划] 规划失败后已清理渲染对象(保留网格可视化)");
|
||||
}
|
||||
}
|
||||
}, "自动路径规划");
|
||||
@ -847,9 +847,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
try
|
||||
{
|
||||
// 清除所有路径可视化,包括自动路径和手动路径
|
||||
PathPointRenderPlugin.Instance.ClearAllPaths();
|
||||
LogManager.Info("重置参数:已清除所有路径可视化显示");
|
||||
// 清除路径可视化,但保留网格可视化
|
||||
PathPointRenderPlugin.Instance.ClearPathsExcept("grid_visualization_all");
|
||||
LogManager.Info("重置参数:已清除路径可视化显示(保留网格可视化)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@ -11,7 +11,7 @@ namespace NavisworksTransport
|
||||
/// <summary>
|
||||
/// 几何提取器 - 使用改进的算法和性能优化
|
||||
/// </summary>
|
||||
public class GeometryExtractor
|
||||
public class GeometryHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 提取模型项的顶视图轮廓
|
||||
Loading…
Reference in New Issue
Block a user