NavisworksTransport/src/PathPlanning/SlopeAnalyzer.cs

542 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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

using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils.CoordinateSystem;
namespace NavisworksTransport.PathPlanning
{
/// <summary>
/// 坡度分析器
/// 负责分析通道的坡度特征和高度变化规律
/// </summary>
public class SlopeAnalyzer
{
private readonly Dictionary<string, ChannelSlopeInfo> _slopeCache;
private readonly double _minSlopeAngle; // 最小坡度角度(弧度)
private readonly ICoordinateSystem _coordinateSystem;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="minSlopeAngle">最小坡度角度(度),小于此角度认为是平面</param>
public SlopeAnalyzer(double minSlopeAngle = 1.0)
{
_slopeCache = new Dictionary<string, ChannelSlopeInfo>();
_minSlopeAngle = minSlopeAngle * Math.PI / 180.0; // 转换为弧度
_coordinateSystem = CoordinateSystemManager.Instance.Current;
}
/// <summary>
/// 分析通道类型和坡度特征
/// </summary>
/// <param name="channel">通道模型项</param>
/// <returns>通道坡度信息</returns>
public ChannelSlopeInfo AnalyzeChannelSlope(ModelItem channel)
{
try
{
if (channel?.Geometry?.BoundingBox == null)
{
LogManager.Warning("[坡度分析] 通道没有有效的几何信息");
return CreateDefaultSlopeInfo();
}
LogManager.Debug($"[坡度分析] 开始分析通道坡度: {channel.DisplayName}");
// 检查缓存
var cacheKey = channel.InstanceGuid.ToString();
if (_slopeCache.TryGetValue(cacheKey, out var cachedInfo))
{
LogManager.Debug($"[坡度分析] 使用缓存的坡度信息: 角度={cachedInfo.SlopeAngle * 180 / Math.PI:F2}度, 类型={cachedInfo.Type}");
return cachedInfo;
}
// 分析通道几何特征
var slopeInfo = AnalyzeGeometrySlope(channel);
// 缓存结果
_slopeCache[cacheKey] = slopeInfo;
LogManager.Info($"[坡度分析] 通道坡度分析完成: 角度={slopeInfo.SlopeAngle * 180 / Math.PI:F2}度, 类型={slopeInfo.Type}, 方向=({slopeInfo.SlopeDirection.X:F2}, {slopeInfo.SlopeDirection.Y:F2}, {slopeInfo.SlopeDirection.Z:F2})");
return slopeInfo;
}
catch (Exception ex)
{
LogManager.Error($"[坡度分析] 分析通道坡度时发生错误: {ex.Message}");
return CreateDefaultSlopeInfo();
}
}
/// <summary>
/// 根据坡度计算精确Z坐标
/// </summary>
/// <param name="position">目标位置</param>
/// <param name="slopeInfo">坡度信息</param>
/// <param name="baseHeight">基准高度</param>
/// <returns>精确的Z坐标</returns>
public double CalculateAccurateZCoordinate(Point3D position, ChannelSlopeInfo slopeInfo, double baseHeight)
{
try
{
LogManager.Debug($"[坡度分析] 计算位置 ({position.X:F2}, {position.Y:F2}) 的精确Z坐标");
// 对于平面通道,直接返回基准高度
if (slopeInfo.Type == SlopeType.Flat || Math.Abs(slopeInfo.SlopeAngle) < _minSlopeAngle)
{
return baseHeight;
}
// 根据坡度类型计算Z坐标
switch (slopeInfo.Type)
{
case SlopeType.Ramp:
return CalculateRampHeight(position, slopeInfo, baseHeight);
case SlopeType.Stairs:
return CalculateStairsHeight(position, slopeInfo, baseHeight);
case SlopeType.CurvedRamp:
return CalculateCurvedRampHeight(position, slopeInfo, baseHeight);
default:
return CalculateLinearSlopeHeight(position, slopeInfo, baseHeight);
}
}
catch (Exception ex)
{
LogManager.Warning($"[坡度分析] 计算精确Z坐标时发生错误: {ex.Message},使用基准高度");
return baseHeight;
}
}
/// <summary>
/// 分析几何体的坡度特征
/// </summary>
/// <param name="channel">通道模型项</param>
/// <returns>坡度信息</returns>
private ChannelSlopeInfo AnalyzeGeometrySlope(ModelItem channel)
{
var bounds = channel.Geometry.BoundingBox;
var slopeInfo = new ChannelSlopeInfo
{
Type = DetermineSlopeType(channel),
SlopePoints = new List<Point3D>()
};
// 添加边界点作为坡度采样点
AddBoundaryPoints(slopeInfo, bounds);
// 计算坡度角度和方向
CalculateSlopeAngleAndDirection(slopeInfo, bounds);
// 根据通道类型进行特殊处理
SpecializedSlopeAnalysis(channel, slopeInfo);
return slopeInfo;
}
/// <summary>
/// 确定坡度类型
/// </summary>
/// <param name="channel">通道模型项</param>
/// <returns>坡度类型</returns>
private SlopeType DetermineSlopeType(ModelItem channel)
{
var displayName = channel.DisplayName?.ToLower() ?? "";
var className = channel.ClassDisplayName?.ToLower() ?? "";
if (displayName.Contains("楼梯") || displayName.Contains("stair") ||
className.Contains("楼梯") || className.Contains("stair"))
{
return SlopeType.Stairs;
}
if (displayName.Contains("坡道") || displayName.Contains("ramp") ||
className.Contains("坡道") || className.Contains("ramp"))
{
return SlopeType.Ramp;
}
// 根据几何特征判断
var bounds = channel.Geometry.BoundingBox;
var (minElevation, maxElevation) = _coordinateSystem.GetHeightRange(bounds);
var (minH1, maxH1, minH2, maxH2) = _coordinateSystem.GetHorizontalRange(bounds);
var heightDiff = maxElevation - minElevation;
var horizontalLength = Math.Max(maxH1 - minH1, maxH2 - minH2);
if (heightDiff > 0.1 && horizontalLength > 0.1) // 有明显高度差
{
var slopeRatio = heightDiff / horizontalLength;
if (slopeRatio > 0.5) // 坡度大于50%,可能是楼梯
{
return SlopeType.Stairs;
}
else if (slopeRatio > 0.05) // 坡度大于5%,可能是坡道
{
return SlopeType.Ramp;
}
}
return SlopeType.Flat; // 默认为平面
}
/// <summary>
/// 添加边界点
/// </summary>
/// <param name="slopeInfo">坡度信息</param>
/// <param name="bounds">边界框</param>
private void AddBoundaryPoints(ChannelSlopeInfo slopeInfo, BoundingBox3D bounds)
{
// 添加8个边界框顶点
var (minElevation, maxElevation) = _coordinateSystem.GetHeightRange(bounds);
var (minH1, maxH1, minH2, maxH2) = _coordinateSystem.GetHorizontalRange(bounds);
slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(minH1, minH2, minElevation));
slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(maxH1, minH2, minElevation));
slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(minH1, maxH2, minElevation));
slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(maxH1, maxH2, minElevation));
slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(minH1, minH2, maxElevation));
slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(maxH1, minH2, maxElevation));
slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(minH1, maxH2, maxElevation));
slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(maxH1, maxH2, maxElevation));
// 添加中心线上的采样点
int sampleCount = 5;
for (int i = 0; i < sampleCount; i++)
{
double ratio = (double)i / (sampleCount - 1);
var samplePoint = _coordinateSystem.CreatePoint(
minH1 + (maxH1 - minH1) * ratio,
minH2 + (maxH2 - minH2) * ratio,
minElevation + (maxElevation - minElevation) * ratio);
slopeInfo.SlopePoints.Add(samplePoint);
}
}
/// <summary>
/// 计算坡度角度和方向
/// </summary>
/// <param name="slopeInfo">坡度信息</param>
/// <param name="bounds">边界框</param>
private void CalculateSlopeAngleAndDirection(ChannelSlopeInfo slopeInfo, BoundingBox3D bounds)
{
// 计算主要方向向量
var (minElevation, maxElevation) = _coordinateSystem.GetHeightRange(bounds);
var (minH1, maxH1, minH2, maxH2) = _coordinateSystem.GetHorizontalRange(bounds);
var directionH1 = maxH1 - minH1;
var directionH2 = maxH2 - minH2;
var directionElevation = maxElevation - minElevation;
// 选择主要的水平方向
var horizontalLength = Math.Max(Math.Abs(directionH1), Math.Abs(directionH2));
if (horizontalLength > 0.001)
{
// 计算坡度角度
slopeInfo.SlopeAngle = Math.Atan(Math.Abs(directionElevation) / horizontalLength);
// 计算坡度方向向量
var directionPoint = _coordinateSystem.CreatePoint(directionH1, directionH2, directionElevation);
var length = Math.Sqrt(directionPoint.X * directionPoint.X + directionPoint.Y * directionPoint.Y + directionPoint.Z * directionPoint.Z);
if (length > 0.001)
{
slopeInfo.SlopeDirection = new Vector3D(
directionPoint.X / length,
directionPoint.Y / length,
directionPoint.Z / length
);
}
else
{
slopeInfo.SlopeDirection = new Vector3D(0, 0, 1); // 默认向上
}
}
else
{
slopeInfo.SlopeAngle = 0;
var up = _coordinateSystem.UpVector;
slopeInfo.SlopeDirection = new Vector3D(up.X, up.Y, up.Z);
}
}
/// <summary>
/// 特殊坡度分析
/// </summary>
/// <param name="channel">通道模型项</param>
/// <param name="slopeInfo">坡度信息</param>
private void SpecializedSlopeAnalysis(ModelItem channel, ChannelSlopeInfo slopeInfo)
{
switch (slopeInfo.Type)
{
case SlopeType.Stairs:
AnalyzeStairsDetails(channel, slopeInfo);
break;
case SlopeType.Ramp:
AnalyzeRampDetails(channel, slopeInfo);
break;
}
}
/// <summary>
/// 分析楼梯详细信息
/// </summary>
/// <param name="channel">通道模型项</param>
/// <param name="slopeInfo">坡度信息</param>
private void AnalyzeStairsDetails(ModelItem channel, ChannelSlopeInfo slopeInfo)
{
try
{
var bounds = channel.Geometry.BoundingBox;
var (minElevation, maxElevation) = _coordinateSystem.GetHeightRange(bounds);
var (minH1, maxH1, minH2, maxH2) = _coordinateSystem.GetHorizontalRange(bounds);
var totalHeight = maxElevation - minElevation;
var totalLength = Math.Max(maxH1 - minH1, maxH2 - minH2);
// 估算楼梯级数假设每级高度15-20cm
var estimatedSteps = (int)(totalHeight / 0.175); // 平均17.5cm每级
if (estimatedSteps < 1) estimatedSteps = 1;
slopeInfo.StepCount = estimatedSteps;
slopeInfo.StepHeight = totalHeight / estimatedSteps;
slopeInfo.StepDepth = totalLength / estimatedSteps;
LogManager.Debug($"[坡度分析] 楼梯详细信息: 级数={estimatedSteps}, 每级高度={slopeInfo.StepHeight:F3}m, 每级深度={slopeInfo.StepDepth:F3}m");
}
catch (Exception ex)
{
LogManager.Warning($"[坡度分析] 分析楼梯详细信息时发生错误: {ex.Message}");
}
}
/// <summary>
/// 分析坡道详细信息
/// </summary>
/// <param name="channel">通道模型项</param>
/// <param name="slopeInfo">坡度信息</param>
private void AnalyzeRampDetails(ModelItem channel, ChannelSlopeInfo slopeInfo)
{
try
{
var bounds = channel.Geometry.BoundingBox;
// 检查是否为弯曲坡道(通过长宽比判断)
var (minH1, maxH1, minH2, maxH2) = _coordinateSystem.GetHorizontalRange(bounds);
var width = Math.Min(maxH1 - minH1, maxH2 - minH2);
var length = Math.Max(maxH1 - minH1, maxH2 - minH2);
if (width > 0 && length / width > 3.0) // 长宽比大于3:1可能是弯曲坡道
{
slopeInfo.Type = SlopeType.CurvedRamp;
LogManager.Debug($"[坡度分析] 检测到弯曲坡道: 长度={length:F2}m, 宽度={width:F2}m");
}
}
catch (Exception ex)
{
LogManager.Warning($"[坡度分析] 分析坡道详细信息时发生错误: {ex.Message}");
}
}
/// <summary>
/// 计算坡道高度
/// </summary>
/// <param name="position">位置点</param>
/// <param name="slopeInfo">坡度信息</param>
/// <param name="baseHeight">基准高度</param>
/// <returns>计算得到的高度</returns>
private double CalculateRampHeight(Point3D position, ChannelSlopeInfo slopeInfo, double baseHeight)
{
return CalculateLinearSlopeHeight(position, slopeInfo, baseHeight);
}
/// <summary>
/// 计算楼梯高度
/// </summary>
/// <param name="position">位置点</param>
/// <param name="slopeInfo">坡度信息</param>
/// <param name="baseHeight">基准高度</param>
/// <returns>计算得到的高度</returns>
private double CalculateStairsHeight(Point3D position, ChannelSlopeInfo slopeInfo, double baseHeight)
{
if (slopeInfo.StepCount <= 0 || slopeInfo.StepHeight <= 0)
{
return CalculateLinearSlopeHeight(position, slopeInfo, baseHeight);
}
// 找到楼梯的起点和终点
var startPoint = slopeInfo.SlopePoints.OrderBy(p => _coordinateSystem.GetElevation(p)).First();
var endPoint = slopeInfo.SlopePoints.OrderBy(p => _coordinateSystem.GetElevation(p)).Last();
// 计算位置在楼梯长度方向上的投影
var (startH1, startH2) = _coordinateSystem.GetHorizontalCoords(startPoint);
var (endH1, endH2) = _coordinateSystem.GetHorizontalCoords(endPoint);
var (positionH1, positionH2) = _coordinateSystem.GetHorizontalCoords(position);
var totalDistance = Math.Sqrt(
Math.Pow(endH1 - startH1, 2) +
Math.Pow(endH2 - startH2, 2)
);
var currentDistance = Math.Sqrt(
Math.Pow(positionH1 - startH1, 2) +
Math.Pow(positionH2 - startH2, 2)
);
if (totalDistance <= 0.001)
{
return baseHeight;
}
// 计算当前位置对应的楼梯级数
var ratio = currentDistance / totalDistance;
var currentStep = (int)(ratio * slopeInfo.StepCount);
// 楼梯的高度是阶梯状的
return baseHeight + currentStep * slopeInfo.StepHeight;
}
/// <summary>
/// 计算弯曲坡道高度
/// </summary>
/// <param name="position">位置点</param>
/// <param name="slopeInfo">坡度信息</param>
/// <param name="baseHeight">基准高度</param>
/// <returns>计算得到的高度</returns>
private double CalculateCurvedRampHeight(Point3D position, ChannelSlopeInfo slopeInfo, double baseHeight)
{
// 弯曲坡道使用样条插值,这里先简化为线性插值
return CalculateLinearSlopeHeight(position, slopeInfo, baseHeight);
}
/// <summary>
/// 计算线性坡度高度
/// </summary>
/// <param name="position">位置点</param>
/// <param name="slopeInfo">坡度信息</param>
/// <param name="baseHeight">基准高度</param>
/// <returns>计算得到的高度</returns>
private double CalculateLinearSlopeHeight(Point3D position, ChannelSlopeInfo slopeInfo, double baseHeight)
{
if (Math.Abs(slopeInfo.SlopeAngle) < _minSlopeAngle)
{
return baseHeight;
}
// 计算沿坡度方向的距离
var projectedDistance = position.X * slopeInfo.SlopeDirection.X +
position.Y * slopeInfo.SlopeDirection.Y;
// 根据坡度角度计算高度增量
var heightIncrement = projectedDistance * Math.Tan(slopeInfo.SlopeAngle);
return baseHeight + heightIncrement;
}
/// <summary>
/// 创建默认坡度信息
/// </summary>
/// <returns>默认坡度信息</returns>
private ChannelSlopeInfo CreateDefaultSlopeInfo()
{
return new ChannelSlopeInfo
{
Type = SlopeType.Flat,
SlopeAngle = 0,
SlopeDirection = new Vector3D(0, 0, 1),
SlopePoints = new List<Point3D>()
};
}
/// <summary>
/// 清除坡度缓存
/// </summary>
public void ClearCache()
{
_slopeCache.Clear();
LogManager.Info("[坡度分析] 坡度缓存已清除");
}
/// <summary>
/// 获取缓存统计信息
/// </summary>
/// <returns>缓存项数量</returns>
public int GetCacheCount()
{
return _slopeCache.Count;
}
}
/// <summary>
/// 通道坡度信息
/// </summary>
public class ChannelSlopeInfo
{
/// <summary>
/// 坡度角度(弧度)
/// </summary>
public double SlopeAngle { get; set; }
/// <summary>
/// 坡度类型
/// </summary>
public SlopeType Type { get; set; }
/// <summary>
/// 坡度方向向量(单位向量)
/// </summary>
public Vector3D SlopeDirection { get; set; }
/// <summary>
/// 坡度特征点集合
/// </summary>
public List<Point3D> SlopePoints { get; set; } = new List<Point3D>();
/// <summary>
/// 楼梯级数(仅对楼梯有效)
/// </summary>
public int StepCount { get; set; }
/// <summary>
/// 每级楼梯高度(仅对楼梯有效)
/// </summary>
public double StepHeight { get; set; }
/// <summary>
/// 每级楼梯深度(仅对楼梯有效)
/// </summary>
public double StepDepth { get; set; }
}
/// <summary>
/// 坡度类型枚举
/// </summary>
public enum SlopeType
{
/// <summary>
/// 平面
/// </summary>
Flat,
/// <summary>
/// 直线坡道
/// </summary>
Ramp,
/// <summary>
/// 楼梯
/// </summary>
Stairs,
/// <summary>
/// 弯曲坡道
/// </summary>
CurvedRamp,
/// <summary>
/// 其他类型
/// </summary>
Other
}
}