diff --git a/NavisworksTransportPlugin.csproj b/NavisworksTransportPlugin.csproj
index 52aeef5..670764d 100644
--- a/NavisworksTransportPlugin.csproj
+++ b/NavisworksTransportPlugin.csproj
@@ -245,7 +245,7 @@
-
+
diff --git a/doc/design/2026/NavisworksAPI使用方法.md b/doc/design/2026/NavisworksAPI使用方法.md
index 7790f3b..e5b6039 100644
--- a/doc/design/2026/NavisworksAPI使用方法.md
+++ b/doc/design/2026/NavisworksAPI使用方法.md
@@ -1309,7 +1309,7 @@ public class CustomPropertyManager
**日志验证**:
-```
+```text
[FloorAttributeManager] 找到楼层属性分类,索引为: 0
[FloorAttributeManager] ✅ 成功更新现有楼层属性分类 (index=1)
```
diff --git a/src/Core/PathPlanningManager.cs b/src/Core/PathPlanningManager.cs
index 022a3ce..d289df0 100644
--- a/src/Core/PathPlanningManager.cs
+++ b/src/Core/PathPlanningManager.cs
@@ -582,16 +582,16 @@ namespace NavisworksTransport
}
///
- /// 清除3D路径标记
+ /// 清除3D路径标记(保留网格可视化)
///
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 网格可视化方法
+
+ ///
+ /// 在3D视图中可视化网格中的可通行单元格
+ /// 在每个可通行网格的中心绘制一个绿色小球
+ ///
+ /// 要可视化的网格地图
+ 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);
+ }
+ }
+
+ ///
+ /// 清除网格可视化
+ ///
+ 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 静态方法
///
diff --git a/src/Core/PathPointRenderPlugin.cs b/src/Core/PathPointRenderPlugin.cs
index b84d38c..9121607 100644
--- a/src/Core/PathPointRenderPlugin.cs
+++ b/src/Core/PathPointRenderPlugin.cs
@@ -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} 个点标记,无连线");
+ }
}
///
@@ -401,6 +421,34 @@ namespace NavisworksTransport
}
}
+ ///
+ /// 清空路径,但保留指定的路径
+ ///
+ /// 要保留的路径ID列表
+ public void ClearPathsExcept(params string[] excludedPathIds)
+ {
+ lock (_lockObject)
+ {
+ var excludedSet = new HashSet(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();
+ }
+ }
+ }
+
///
/// 构建路径可视化
///
@@ -456,12 +504,15 @@ namespace NavisworksTransport
/// 圆形标记
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
};
}
+ ///
+ /// 获取网格可视化小球的半径(比正常路径点小)
+ ///
+ /// 网格可视化半径
+ private double GetRadiusForGridVisualization()
+ {
+ // 网格可视化小球使用0.2米的物理尺寸,比正常路径点小
+ double baseRadiusInMeters = 0.2;
+ double metersToModelUnits = GetMetersToModelUnitsConversionFactor();
+ return baseRadiusInMeters * metersToModelUnits;
+ }
+
///
/// 获取连线颜色(统一使用橙色)
///
diff --git a/src/Core/Properties/CategoryAttributeManager.cs b/src/Core/Properties/CategoryAttributeManager.cs
index cebd761..95d4359 100644
--- a/src/Core/Properties/CategoryAttributeManager.cs
+++ b/src/Core/Properties/CategoryAttributeManager.cs
@@ -281,6 +281,56 @@ namespace NavisworksTransport
return string.Empty;
}
+ ///
+ /// 获取指定类型的物流项目
+ ///
+ /// 物流元素类型
+ /// 文档,如果为null则使用ActiveDocument
+ /// 指定类型的物流项目列表
+ public static List GetLogisticsItemsByType(LogisticsElementType elementType, Document document = null)
+ {
+ try
+ {
+ // 如果没有提供文档,使用ActiveDocument
+ if (document == null)
+ document = Application.ActiveDocument;
+
+ // 检查文档有效性
+ if (document?.Models == null)
+ {
+ LogManager.Warning($"[CategoryAttributeManager] 无法获取文档模型");
+ return new List();
+ }
+
+ // 直接使用 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();
+ }
+ }
+
///
/// 根据物流属性筛选模型项
///
diff --git a/src/PathPlanning/ChannelBasedGridBuilder.cs b/src/PathPlanning/ChannelBasedGridBuilder.cs
index 6dec996..9efabce 100644
--- a/src/PathPlanning/ChannelBasedGridBuilder.cs
+++ b/src/PathPlanning/ChannelBasedGridBuilder.cs
@@ -18,9 +18,29 @@ namespace NavisworksTransport.PathPlanning
///
public class ChannelBasedGridBuilder
{
- private CategoryAttributeManager _categoryManager;
private ChannelHeightDetector _heightDetector;
+ ///
+ /// 通道几何类型
+ ///
+ private enum ChannelGeometryType
+ {
+ ///
+ /// 水平通道(楼板)
+ ///
+ Horizontal,
+
+ ///
+ /// 倾斜通道(楼梯、坡道)
+ ///
+ Inclined,
+
+ ///
+ /// 复杂形状
+ ///
+ Complex
+ }
+
///
/// 构造函数
///
@@ -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
};
}
- ///
- /// 获取所有通道物品
- ///
- /// 所有模型物品
- /// 通道物品列表
- private List GetChannelItems(List 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();
- }
- catch (Exception ex)
- {
- LogManager.Error($"[通道网格构建器] 获取通道物品时发生错误: {ex.Message}");
- return new List();
- }
- }
///
/// 将通道投影到网格
- /// 核心创新:智能投影策略 - 薄通道优先使用顶面提取,复杂通道使用射线扫描
+ /// 统一投影方式:基于三角形几何的精确投影,解决楼板洞口和不规则形状问题
///
/// 通道物品
/// 目标网格地图
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($"[通道网格构建器] 使用垂直射线扫描(精确投影)");
- }
-
- ///
- /// 方法1:顶面提取投影(快速且精确)
- ///
- /// 模型物品
- /// 网格地图
- /// 三角形列表(可选,避免重复提取)
- private void ProjectUsingTopFaces(ModelItem item, GridMap gridMap, List 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} 个三角形");
- }
-
- ///
- /// 方法2:垂直射线扫描(最精确)
- ///
- /// 模型物品
- /// 网格地图
- 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();
- 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} 个三角形");
}
-
-
-
-
-
///
/// 计算三角形法向量
///
@@ -291,24 +204,6 @@ namespace NavisworksTransport.PathPlanning
return normal;
}
- ///
- /// 统计顶面数量
- ///
- /// 三角形列表
- /// 顶面数量
- private int CountTopFaces(List triangles)
- {
- int count = 0;
- foreach (var triangle in triangles)
- {
- var normal = ComputeTriangleNormal(triangle);
- if (normal.Z > 0.7) // 朝上的面
- {
- count++;
- }
- }
- return count;
- }
///
/// 将三角形光栅化到网格
@@ -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
};
}
+ ///
+ /// 分析通道几何类型
+ ///
+ /// 三角形列表
+ /// 通道几何类型
+ private ChannelGeometryType AnalyzeChannelType(List 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;
+ }
+
///
/// 格式化边界框信息用于日志
///
diff --git a/src/PathPlanning/ChannelHeightDetector.cs b/src/PathPlanning/ChannelHeightDetector.cs
index bcc86df..54c9657 100644
--- a/src/PathPlanning/ChannelHeightDetector.cs
+++ b/src/PathPlanning/ChannelHeightDetector.cs
@@ -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++;
diff --git a/src/PathPlanning/GridMapGenerator.cs b/src/PathPlanning/GridMapGenerator.cs
index 0aba662..2b4867e 100644
--- a/src/PathPlanning/GridMapGenerator.cs
+++ b/src/PathPlanning/GridMapGenerator.cs
@@ -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)
diff --git a/src/PathPlanning/VerticalScanProcessor.cs b/src/PathPlanning/VerticalScanProcessor.cs
index b88c49d..b303b0c 100644
--- a/src/PathPlanning/VerticalScanProcessor.cs
+++ b/src/PathPlanning/VerticalScanProcessor.cs
@@ -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)
diff --git a/src/UI/WPF/ViewModels/PathEditingViewModel.cs b/src/UI/WPF/ViewModels/PathEditingViewModel.cs
index d8d2d1b..9cb6551 100644
--- a/src/UI/WPF/ViewModels/PathEditingViewModel.cs
+++ b/src/UI/WPF/ViewModels/PathEditingViewModel.cs
@@ -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)
{
diff --git a/src/Utils/GeometryExtractor.cs b/src/Utils/GeometryHelper.cs
similarity index 99%
rename from src/Utils/GeometryExtractor.cs
rename to src/Utils/GeometryHelper.cs
index bbb6272..d40632f 100644
--- a/src/Utils/GeometryExtractor.cs
+++ b/src/Utils/GeometryHelper.cs
@@ -11,7 +11,7 @@ namespace NavisworksTransport
///
/// 几何提取器 - 使用改进的算法和性能优化
///
- public class GeometryExtractor
+ public class GeometryHelper
{
///
/// 提取模型项的顶视图轮廓