using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils;

namespace NavisworksTransport.PathPlanning
{
    /// <summary>
            /// 定向包围盒（OBB）
            /// </summary>
            public class OrientedBoundingBox
            {
                /// <summary>
                /// 中心点
                /// </summary>
                public Point3D Center { get; set; }
    
                /// <summary>
                /// 主轴（3个正交向量）
                /// </summary>
                public Point3D[] Axes { get; set; }
    
                /// <summary>
                /// 半长（沿各轴的半长度）
                /// </summary>
                public Point3D Extents { get; set; }
                
                /// <summary>
                /// 投影范围的最小 Z 值（在主轴坐标系中）
                /// </summary>
                public double ProjectedMinZ { get; set; }
                
                /// <summary>
                /// 投影范围的最大 Z 值（在主轴坐标系中）
                /// </summary>
                public double ProjectedMaxZ { get; set; }
        /// <summary>
        /// 获取指定索引的轴
        /// </summary>
        public Point3D GetAxis(int index)
        {
            if (index < 0 || index >= Axes.Length)
                throw new ArgumentOutOfRangeException(nameof(index));
            return Axes[index];
        }

        /// <summary>
        /// 获取指定轴的长度
        /// </summary>
        public double GetAxisLength(int index)
        {
            if (index < 0 || index >= Axes.Length)
                throw new ArgumentOutOfRangeException(nameof(index));
            
            if (index == 0)
                return 2 * Extents.X;
            else if (index == 1)
                return 2 * Extents.Y;
            else if (index == 2)
                return 2 * Extents.Z;
            else
                return 0;
        }
    }

    /// <summary>
            /// 空轨几何体辅助工具
            /// 负责从空轨几何体提取下表面中心线，支持拐弯和倾斜
            /// </summary>
        public static class RailGeometryHelper
        {
            /// <summary>
                    /// 空轨下表面信息
                    /// </summary>
                    public class RailBottomSurfaceInfo        {
            /// <summary>
            /// 下表面三角形列表
            /// </summary>
            public List<Triangle3D> BottomTriangles { get; set; }

            /// <summary>
            /// 包围盒
            /// </summary>
            public BoundingBox3D Bounds { get; set; }

            /// <summary>
            /// 最低Z坐标
            /// </summary>
            public double MinZ { get; set; }

            /// <summary>
            /// 最高Z坐标
            /// </summary>
            public double MaxZ { get; set; }

            /// <summary>
            /// 缓存时间
            /// </summary>
            public DateTime CacheTime { get; set; }
        }

        /// <summary>
        /// 空轨基准路径信息（完整路径）
        /// </summary>
        public class RailBaselinePath
        {
            /// <summary>
            /// 完整的基准路径点列表
            /// </summary>
            public List<Point3D> PathPoints { get; set; }

            /// <summary>
            /// 基准路径的定义方式
            /// </summary>
            public RailPathDefinitionMode DefinitionMode { get; set; }
            
            /// <summary>
            /// 路径总长度（模型单位）
            /// </summary>
            public double TotalLength { get; set; }
            
            /// <summary>
            /// 起点
            /// </summary>
            public Point3D StartPoint { get; set; }
            
            /// <summary>
            /// 终点
            /// </summary>
            public Point3D EndPoint { get; set; }
            
            /// <summary>
            /// 关联的空轨模型项
            /// </summary>
            public ModelItem RailModel { get; set; }
            
            /// <summary>
            /// 创建时间
            /// </summary>
            public DateTime CreatedTime { get; set; }
        }

        /// <summary>
        /// 缓存字典（模型ID -> 下表面信息）
        /// </summary>
        private static readonly Dictionary<string, RailBottomSurfaceInfo> _railCache = 
            new Dictionary<string, RailBottomSurfaceInfo>();

        /// <summary>
        /// 基准路径缓存（模型ID -> 基准路径）
        /// </summary>
        private static readonly Dictionary<string, RailBaselinePath> _baselinePathCache = 
            new Dictionary<string, RailBaselinePath>();

        /// <summary>
        /// 缓存有效期（30分钟）
        /// </summary>
        private static readonly TimeSpan _cacheValidDuration = TimeSpan.FromMinutes(30);

        /// <summary>
        /// 步骤1: 提取空轨完整基准路径（用于可视化和吸附）
        /// </summary>
        /// <param name="railModel">空轨模型项</param>
        /// <param name="samplingInterval">采样间隔（模型单位），默认0.5</param>
        /// <returns>空轨基准路径信息</returns>
        public static RailBaselinePath ExtractRailBaselinePath(
            ModelItem railModel,
            double samplingInterval = 0.5)
        {
            return ExtractRailBaselinePath(railModel, RailPathDefinitionMode.RailCenterLine, samplingInterval);
        }

        /// <summary>
        /// 步骤1: 提取空轨完整基准路径（用于可视化和吸附）
        /// </summary>
        /// <param name="railModel">空轨模型项</param>
        /// <param name="definitionMode">基准线定义模式</param>
        /// <param name="samplingInterval">采样间隔（模型单位）</param>
        /// <returns>空轨基准路径信息</returns>
        public static RailBaselinePath ExtractRailBaselinePath(
            ModelItem railModel,
            RailPathDefinitionMode definitionMode,
            double samplingInterval)
        {
            try
            {
                LogManager.Debug($"[空轨] 步骤1: 开始提取完整基准路径: {railModel.DisplayName}, 模式={definitionMode}");

                // 检查缓存
                var cacheKey = $"baseline_{definitionMode}_{railModel.DisplayName}_{railModel.GetHashCode()}";
                if (_baselinePathCache.TryGetValue(cacheKey, out var cachedPath))
                {
                    // 检查缓存是否过期
                    if (DateTime.Now - cachedPath.CreatedTime < _cacheValidDuration)
                    {
                        LogManager.Debug($"[空轨] 使用缓存的基准路径");
                        return cachedPath;
                    }
                    else
                    {
                        LogManager.Debug($"[空轨] 基准路径缓存已过期，重新提取");
                        _baselinePathCache.Remove(cacheKey);
                    }
                }

                // 使用 OBB 方法提取基准路径
                var pathPoints = definitionMode == RailPathDefinitionMode.RailCenterLine
                    ? ExtractRailCenterLineByOBB(railModel, samplingInterval)
                    : ExtractRailBottomCenterLineByOBB(railModel, samplingInterval);
                LogManager.Debug($"[空轨] 生成 {pathPoints.Count} 个采样点");

                // 计算路径长度
                double totalLength = CalculatePathLength(pathPoints);
                
                // 创建基准路径对象
                var baselinePath = new RailBaselinePath
                {
                    PathPoints = pathPoints,
                    DefinitionMode = definitionMode,
                    TotalLength = totalLength,
                    StartPoint = pathPoints.First(),
                    EndPoint = pathPoints.Last(),
                    RailModel = railModel,
                    CreatedTime = DateTime.Now
                };

                // 加入缓存
                _baselinePathCache[cacheKey] = baselinePath;

                LogManager.Debug($"[空轨] 步骤1完成: 基准路径总长度 {totalLength:F2}，{pathPoints.Count}个采样点，模式={definitionMode}");
                return baselinePath;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[空轨] 步骤1失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 步骤3: 将点吸附到基准路径上（找到最近的路径点）
        /// </summary>
        /// <param name="baselinePath">基准路径</param>
        /// <param name="inputPoint">输入点（用户点击的位置）</param>
        /// <param name="maxSnapDistance">最大吸附距离（模型单位），默认1.0</param>
        /// <param name="snappedPoint">输出参数：吸附后的点</param>
        /// <returns>是否成功吸附</returns>
        public static bool SnapToBaselinePath(
            RailBaselinePath baselinePath,
            Point3D inputPoint,
            double maxSnapDistance,
            out Point3D snappedPoint)
        {
            snappedPoint = new Point3D();
            
            try
            {
                if (baselinePath == null || baselinePath.PathPoints.Count == 0)
                {
                    LogManager.Warning("[空轨] 基准路径为空，无法吸附");
                    return false;
                }

                LogManager.Debug($"[空轨] 步骤3: 吸附点到基准路径，输入点=({inputPoint.X:F2}, {inputPoint.Y:F2}, {inputPoint.Z:F2})");

                Point3D nearestPoint = FindNearestPointOnPath(baselinePath.PathPoints, inputPoint);
                double minDistance = CalculateDistance2D(inputPoint, nearestPoint);

                // 检查是否在最大吸附距离内
                if (minDistance <= maxSnapDistance)
                {
                    snappedPoint = nearestPoint;
                    LogManager.Debug($"[空轨] 步骤3完成: 吸附到 ({nearestPoint.X:F2}, {nearestPoint.Y:F2}, {nearestPoint.Z:F2})，距离 {minDistance:F2}");
                    return true;
                }
                else
                {
                    LogManager.Warning($"[空轨] 步骤3失败: 超出最大吸附距离 {maxSnapDistance:F2}（实际距离 {minDistance:F2}）");
                    return false;
                }
            }
            catch (Exception ex)
            {
                LogManager.Error($"[空轨] 步骤3失败: {ex.Message}");
                return false;
            }
        }

        /// <summary>
        /// 步骤4: 从基准路径提取指定区段的路径点
        /// </summary>
        /// <param name="baselinePath">基准路径</param>
        /// <param name="snappedStartPoint">吸附后的起点</param>
        /// <param name="snappedEndPoint">吸附后的终点</param>
        /// <param name="samplingInterval">采样间隔（模型单位），默认0.5</param>
        /// <returns>路径点列表（包含精确的Z坐标）</returns>
        public static List<Point3D> ExtractPathSegment(
            RailBaselinePath baselinePath,
            Point3D snappedStartPoint,
            Point3D snappedEndPoint,
            double samplingInterval = 0.5)
        {
            try
            {
                LogManager.Debug($"[空轨] 步骤4: 提取路径段");
                LogManager.Debug($"[空轨] 起点: ({snappedStartPoint.X:F2}, {snappedStartPoint.Y:F2}, {snappedStartPoint.Z:F2})");
                LogManager.Debug($"[空轨] 终点: ({snappedEndPoint.X:F2}, {snappedEndPoint.Y:F2}, {snappedEndPoint.Z:F2})");

                // 在基准路径中找到起点和终点的索引
                int startIndex = FindNearestPointIndex(baselinePath.PathPoints, snappedStartPoint);
                int endIndex = FindNearestPointIndex(baselinePath.PathPoints, snappedEndPoint);

                if (startIndex == -1 || endIndex == -1)
                {
                    throw new InvalidOperationException("无法在基准路径中找到起点或终点");
                }

                LogManager.Debug($"[空轨] 起点索引: {startIndex}, 终点索引: {endIndex}");

                // 确保起点在终点之前
                if (startIndex > endIndex)
                {
                    int temp = startIndex;
                    startIndex = endIndex;
                    endIndex = temp;
                    LogManager.Info("[空轨] 交换起点和终点索引");
                }

                // 提取路径段
                var segmentPoints = new List<Point3D>();
                for (int i = startIndex; i <= endIndex; i++)
                {
                    segmentPoints.Add(baselinePath.PathPoints[i]);
                }

                // 如果采样间隔比基准路径的采样间隔大，进行重采样
                double baselineInterval = baselinePath.TotalLength / (baselinePath.PathPoints.Count - 1);
                if (samplingInterval > baselineInterval * 1.5)
                {
                    LogManager.Debug($"[空轨] 需要重采样: 基准间隔{baselineInterval:F3}，目标间隔{samplingInterval:F3}");
                    segmentPoints = ResamplePath(segmentPoints, samplingInterval);
                }

                LogManager.Debug($"[空轨] 步骤4完成: 提取了 {segmentPoints.Count} 个路径点");
                return segmentPoints;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[空轨] 步骤4失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 找到最近的路径点索引
        /// </summary>
        private static int FindNearestPointIndex(List<Point3D> pathPoints, Point3D targetPoint)
        {
            int nearestIndex = -1;
            double minDistance = double.MaxValue;

            for (int i = 0; i < pathPoints.Count; i++)
            {
                double distance = CalculateDistance2D(pathPoints[i], targetPoint);
                if (distance < minDistance)
                {
                    minDistance = distance;
                    nearestIndex = i;
                }
            }

            return nearestIndex;
        }

        /// <summary>
        /// 找到路径上距离目标点最近的投影点
        /// </summary>
        private static Point3D FindNearestPointOnPath(List<Point3D> pathPoints, Point3D targetPoint)
        {
            if (pathPoints == null || pathPoints.Count == 0)
            {
                return new Point3D();
            }

            if (pathPoints.Count == 1)
            {
                return pathPoints[0];
            }

            Point3D nearestPoint = pathPoints[0];
            double minDistance = double.MaxValue;

            for (int i = 0; i < pathPoints.Count - 1; i++)
            {
                var projectedPoint = ProjectPointToSegment(targetPoint, pathPoints[i], pathPoints[i + 1]);
                double distance = CalculateDistance2D(targetPoint, projectedPoint);
                if (distance < minDistance)
                {
                    minDistance = distance;
                    nearestPoint = projectedPoint;
                }
            }

            return nearestPoint;
        }

        /// <summary>
        /// 将点投影到线段上
        /// </summary>
        private static Point3D ProjectPointToSegment(Point3D point, Point3D segmentStart, Point3D segmentEnd)
        {
            double segmentX = segmentEnd.X - segmentStart.X;
            double segmentY = segmentEnd.Y - segmentStart.Y;
            double segmentZ = segmentEnd.Z - segmentStart.Z;

            double segmentLengthSquared = segmentX * segmentX + segmentY * segmentY + segmentZ * segmentZ;
            if (segmentLengthSquared < 1e-9)
            {
                return segmentStart;
            }

            double pointX = point.X - segmentStart.X;
            double pointY = point.Y - segmentStart.Y;
            double pointZ = point.Z - segmentStart.Z;

            double projection = (pointX * segmentX + pointY * segmentY + pointZ * segmentZ) / segmentLengthSquared;
            projection = Math.Max(0.0, Math.Min(1.0, projection));

            return new Point3D(
                segmentStart.X + projection * segmentX,
                segmentStart.Y + projection * segmentY,
                segmentStart.Z + projection * segmentZ);
        }

        /// <summary>
        /// 重采样路径
        /// </summary>
        private static List<Point3D> ResamplePath(List<Point3D> path, double newInterval)
        {
            double totalLength = CalculatePathLength(path);
            int sampleCount = Math.Max(2, (int)Math.Ceiling(totalLength / newInterval));

            var resampled = new List<Point3D>();
            for (int i = 0; i < sampleCount; i++)
            {
                double progress = (double)i / (sampleCount - 1);
                var point = InterpolateOnPath(path, progress);
                resampled.Add(point);
            }

            return resampled;
        }

        /// <summary>
        /// 清除缓存
        /// </summary>
        public static void ClearCache()
        {
            _railCache.Clear();
            _baselinePathCache.Clear();
            LogManager.Debug("[空轨] 缓存已清除");
        }

        /// <summary>
        /// 清除指定模型的基准路径缓存
        /// </summary>
        public static void ClearBaselinePathCache(ModelItem railModel)
        {
            var legacyCacheKey = $"baseline_{RailPathDefinitionMode.LegacySuspensionPoint}_{railModel.DisplayName}_{railModel.GetHashCode()}";
            var centerCacheKey = $"baseline_{RailPathDefinitionMode.RailCenterLine}_{railModel.DisplayName}_{railModel.GetHashCode()}";
            _baselinePathCache.Remove(legacyCacheKey);
            _baselinePathCache.Remove(centerCacheKey);
            LogManager.Debug($"[空轨] 已清除模型 {railModel.DisplayName} 的基准路径缓存");
        }

        /// <summary>
        /// 提取并渲染所有空轨模型的基准路径
        /// </summary>
        /// <returns>成功提取的基准路径数量</returns>
        public static int ExtractAndRenderAllRailBaselines()
        {
            // 确保在主线程上执行（Navisworks COM API 要求）
            if (!NavisworksApiHelper.IsOnUIThread())
            {
                return NavisworksApiHelper.ExecuteOnUIThread(ExtractAndRenderAllRailBaselines);
            }

            try
            {
                LogManager.Info("[空轨] 开始提取所有空轨模型的基准路径");

                // 使用现成的辅助方法获取所有空轨模型
                var railItems = CategoryAttributeManager.GetLogisticsItemsByType("空轨");

                if (railItems == null || railItems.Count == 0)
                {
                    LogManager.Info("[空轨] 没有找到设置为空轨属性的模型");
                    return 0;
                }

                LogManager.Info($"[空轨] 找到 {railItems.Count} 个空轨模型");

                // 提取每个空轨模型的基准路径
                int successCount = 0;
                foreach (var item in railItems)
                {
                    try
                    {
                        var baselinePath = ExtractRailBaselinePath(item);
                        if (baselinePath != null && baselinePath.PathPoints != null && baselinePath.PathPoints.Count > 0)
                        {
                            successCount++;
                            LogManager.Debug($"[空轨] 成功提取模型 {item.DisplayName} 的基准路径，共 {baselinePath.PathPoints.Count} 个点");

                            // 渲染基准路径
                            var railModelId = $"{item.DisplayName}_{item.GetHashCode()}";
                            PathPointRenderPlugin.Instance.RenderRailBaseline(railModelId, baselinePath.PathPoints);
                        }
                        else
                        {
                            LogManager.Warning($"[空轨] 模型 {item.DisplayName} 的基准路径提取失败或路径为空");
                        }
                    }
                    catch (Exception ex)
                    {
                        LogManager.Error($"[空轨] 提取模型 {item.DisplayName} 的基准路径时发生错误: {ex.Message}");
                    }
                }

                LogManager.Info($"[空轨] 基准路径提取完成，成功 {successCount} 个，失败 {railItems.Count - successCount} 个");
                return successCount;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[空轨] 提取所有空轨基准路径失败: {ex.Message}");
                return 0;
            }
        }
        
        /// <summary>
        /// 使用 OBB 包围体提取空轨参考中心线
        /// </summary>
        /// <param name="railModel">空轨模型项</param>
        /// <param name="samplingInterval">采样间隔（模型单位），默认0.5</param>
        /// <returns>路径点列表（轨道参考中心线）</returns>
        public static List<Point3D> ExtractRailCenterLineByOBB(
            ModelItem railModel,
            double samplingInterval = 0.5)
        {
            try
            {
                LogManager.Debug($"[空轨] 使用 OBB 方法提取轨道参考中心线: {railModel.DisplayName}");

                var obb = ComputeOrientedBoundingBox(railModel);
                var geometryCenterLine = TryExtractDualRailCenterLine(railModel, obb, samplingInterval);
                if (geometryCenterLine != null && geometryCenterLine.Count >= 2)
                {
                    LogManager.Debug($"[空轨] 双轨几何中心线提取完成: 采样点数={geometryCenterLine.Count}");
                    return geometryCenterLine;
                }

                LogManager.Warning("[空轨] 双轨几何中心线提取失败，退回 OBB 主轴中线");
                return BuildCenterLineFromOffsets(obb, 0.0, 0.0, samplingInterval);
            }
            catch (Exception ex)
            {
                LogManager.Error($"[空轨] 提取轨道参考中心线失败: {ex.Message}");
                throw;
            }
        }

        private static List<Point3D> TryExtractDualRailCenterLine(
            ModelItem railModel,
            OrientedBoundingBox obb,
            double samplingInterval)
        {
            var triangles = GeometryHelper.ExtractTriangles(new[] { railModel });
            var pointSet = new HashSet<Point3D>();
            foreach (var triangle in triangles)
            {
                pointSet.Add(triangle.Point1);
                pointSet.Add(triangle.Point2);
                pointSet.Add(triangle.Point3);
            }

            var points = pointSet.ToList();
            if (points.Count < 20)
            {
                LogManager.Warning($"[空轨] 双轨几何中心线样本不足: {points.Count}");
                return null;
            }

            int verticalAxisIndex = GetMostVerticalAxisIndex(obb);
            int acrossAxisIndex = verticalAxisIndex == 1 ? 2 : 1;

            var localPoints = points
                .Select(point => ProjectPointToObbLocal(point, obb))
                .ToList();

            var railBandPoints = ExtractDominantRailBand(localPoints, verticalAxisIndex);
            if (railBandPoints.Count < 10)
            {
                LogManager.Warning($"[空轨] 双轨几何中心线有效轨面点不足: {railBandPoints.Count}");
                return null;
            }

            double centerAcross = EstimateDualRailAcrossCenter(railBandPoints, acrossAxisIndex, obb.GetAxisLength(acrossAxisIndex));
            double centerVertical = railBandPoints.Average(point => GetAxisValue(point, verticalAxisIndex));

            LogManager.Debug($"[空轨] 双轨几何中心估计: across={centerAcross:F3}, vertical={centerVertical:F3}, verticalAxis={verticalAxisIndex}, acrossAxis={acrossAxisIndex}");
            return BuildCenterLineFromOffsets(obb, centerAcross, centerVertical, samplingInterval, acrossAxisIndex, verticalAxisIndex);
        }

        private static List<Point3D> BuildCenterLineFromOffsets(
            OrientedBoundingBox obb,
            double acrossOffset,
            double verticalOffset,
            double samplingInterval,
            int acrossAxisIndex = 1,
            int verticalAxisIndex = 2)
        {
            var principalAxis = obb.GetAxis(0);
            var principalLength = obb.GetAxisLength(0);
            var halfLength = principalLength / 2.0;
            var acrossAxis = obb.GetAxis(acrossAxisIndex);
            var verticalAxis = obb.GetAxis(verticalAxisIndex);

            var offsetCenter = new Point3D(
                obb.Center.X + acrossAxis.X * acrossOffset + verticalAxis.X * verticalOffset,
                obb.Center.Y + acrossAxis.Y * acrossOffset + verticalAxis.Y * verticalOffset,
                obb.Center.Z + acrossAxis.Z * acrossOffset + verticalAxis.Z * verticalOffset);

            var start = new Point3D(
                offsetCenter.X - principalAxis.X * halfLength,
                offsetCenter.Y - principalAxis.Y * halfLength,
                offsetCenter.Z - principalAxis.Z * halfLength);
            var end = new Point3D(
                offsetCenter.X + principalAxis.X * halfLength,
                offsetCenter.Y + principalAxis.Y * halfLength,
                offsetCenter.Z + principalAxis.Z * halfLength);

            var sampledPath = SampleLine(start, end, samplingInterval);
            LogManager.Debug($"[空轨] 轨道参考中心线提取完成: 主轴长度={principalLength:F2}, 采样点数={sampledPath.Count}");
            return sampledPath;
        }

        /// <summary>
        /// 使用 OBB 包围体提取空轨下表面中心线（旧方法，支持倾斜）
        /// </summary>
        /// <param name="railModel">空轨模型项</param>
        /// <param name="samplingInterval">采样间隔（模型单位），默认0.5</param>
        /// <returns>路径点列表（下表面中心线）</returns>
        public static List<Point3D> ExtractRailBottomCenterLineByOBB(
            ModelItem railModel,
            double samplingInterval = 0.5)
        {
            try
            {
                LogManager.Debug($"[空轨] 使用 OBB 方法提取下表面中心线: {railModel.DisplayName}");
                LogManager.Debug($"[空轨] 采样间隔: {samplingInterval:F2}");

                // 1. 计算 OBB 包围体
                var obb = ComputeOrientedBoundingBox(railModel);
                
                // 2. 获取主轴（最长边，通常是空轨的长度方向）
                var principalAxis = obb.GetAxis(0);
                var axisLength = obb.GetAxisLength(0);
                var principalAxisVector = principalAxis.ToVector3D();
                
                LogManager.Debug($"[空轨] OBB 主轴长度: {axisLength:F2}（模型单位）");
                LogManager.Debug($"[空轨] 主轴方向: ({principalAxisVector.X:F3}, {principalAxisVector.Y:F3}, {principalAxisVector.Z:F3})");

                // 3. 计算中心线的起点和终点（下表面）
                var center = obb.Center;
                var direction = principalAxis.ToVector3D();
                
                // 找到 Z 轴方向的轴（使用点积绝对值最大法）
                var zAxisDirection = new Vector3D(0, 0, 1); // Z 轴向上方向
                
                // 计算所有轴与 Z 轴方向的点积绝对值
                var absDotProducts = new double[2];
                for (int i = 1; i < 3; i++)
                {
                    var axis = obb.GetAxis(i);
                    var axisVector = axis.ToVector3D();
                    
                    // 计算点积：axis · zAxisDirection
                    var dotProduct = axisVector.X * zAxisDirection.X + 
                                         axisVector.Y * zAxisDirection.Y + 
                                         axisVector.Z * zAxisDirection.Z;
                    absDotProducts[i - 1] = Math.Abs(dotProduct);
                    
                    LogManager.Debug($"[空轨] Axis {i} 与 Z 轴方向点积: {dotProduct:F3}, 绝对值: {absDotProducts[i - 1]:F3}");
                }
                
                // 选择点积绝对值最大的轴（最接近 Z 轴方向）
                int downAxisIndex = absDotProducts[1] > absDotProducts[0] ? 2 : 1;
                
                var downAxis = obb.GetAxis(downAxisIndex);
                var downAxisVector = downAxis.ToVector3D();
                
                LogManager.Debug($"[空轨] 下表面轴索引: {downAxisIndex}, 方向: ({downAxisVector.X:F3}, {downAxisVector.Y:F3}, {downAxisVector.Z:F3})");
                
                // 计算下表面偏移（使用半长度）
                var downHalfLength = obb.GetAxisLength(downAxisIndex) / 2;
                var downOffset = downAxisVector * downHalfLength;
                
                LogManager.Debug($"[空轨] 下表面偏移向量: ({downOffset.X:F3}, {downOffset.Y:F3}, {downOffset.Z:F3})");
                
                // 中心线起点和终点
                var startCenter = center - direction * (axisLength / 2) + downOffset;
                var endCenter = center + direction * (axisLength / 2) + downOffset;
                
                LogManager.Debug($"[空轨] 中心线起点: ({startCenter.X:F2}, {startCenter.Y:F2}, {startCenter.Z:F2})");
                LogManager.Debug($"[空轨] 中心线终点: ({endCenter.X:F2}, {endCenter.Y:F2}, {endCenter.Z:F2})");

                // 4. 沿中心线采样
                var aabb = railModel.BoundingBox();
                var baseline = SampleLine(startCenter, endCenter, samplingInterval);
                
                LogManager.Debug($"[空轨] OBB 方法完成，生成 {baseline.Count} 个采样点");
                
                // 5. 使用射线投射获取下表面的精确高度（处理不平整的下表面）
                var triangles = GeometryHelper.ExtractTriangles(new[] { railModel });
                var bottomTriangles = FilterBottomTriangles(triangles);
                
                if (bottomTriangles.Count > 0)
                {
                    var adjustedBaseline = new List<Point3D>();
                    int successCount = 0;
                    
                    foreach (var point in baseline)
                    {
                        try
                        {
                            double bottomZ = GetBottomHeightByRaycast(
                                point, 
                                bottomTriangles, 
                                aabb.Min.Z);
                            
                            if (bottomZ > aabb.Min.Z - 1000)
                            {
                                adjustedBaseline.Add(new Point3D(point.X, point.Y, bottomZ));
                                successCount++;
                            }
                        }
                        catch (Exception ex)
                        {
                            LogManager.Warning($"[空轨] 射线投射失败: {ex.Message}");
                            adjustedBaseline.Add(point); // 使用原始点作为后备
                        }
                    }
                    
                    LogManager.Debug($"[空轨] 射线投射调整: {successCount}/{baseline.Count} 个点已调整");
                    
                    if (adjustedBaseline.Count >= 2)
                    {
                        return adjustedBaseline;
                    }
                }
                
                return baseline;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[空轨] OBB 方法提取下表面中心线失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 计算 OBB 包围体（使用 PCA 方法）
        /// </summary>
        private static OrientedBoundingBox ComputeOrientedBoundingBox(ModelItem modelItem)
        {
            try
            {
                // 输出 ModelItem 的 AABB 包围盒信息
                var aabb = modelItem.BoundingBox();
                LogManager.Debug($"[空轨] AABB包围盒: Min({aabb.Min.X:F2}, {aabb.Min.Y:F2}, {aabb.Min.Z:F2}), Max({aabb.Max.X:F2}, {aabb.Max.Y:F2}, {aabb.Max.Z:F2})");
                LogManager.Debug($"[空轨] AABB尺寸: X={aabb.Max.X - aabb.Min.X:F2}, Y={aabb.Max.Y - aabb.Min.Y:F2}, Z={aabb.Max.Z - aabb.Min.Z:F2}");
                
                // 使用 GeometryHelper 提取三角形
                var triangles = GeometryHelper.ExtractTriangles(new[] { modelItem });
                
                // 从三角形中提取所有顶点（去重）
                var pointSet = new HashSet<Point3D>();
                foreach (var triangle in triangles)
                {
                    pointSet.Add(triangle.Point1);
                    pointSet.Add(triangle.Point2);
                    pointSet.Add(triangle.Point3);
                }
                
                var points = pointSet.ToList();

                if (points.Count == 0)
                {
                    throw new InvalidOperationException($"模型 {modelItem.DisplayName} 没有几何体顶点");
                }
                
                LogManager.Debug($"[空轨] 提取到 {points.Count} 个唯一顶点");

                // 使用 PCA 计算 OBB
                return ComputeOBBByPCA(points);
            }
            catch (Exception ex)
            {
                LogManager.Error($"[空轨] 计算 OBB 失败: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 使用 PCA 计算 OBB
        /// </summary>
        private static OrientedBoundingBox ComputeOBBByPCA(List<Point3D> points)
        {
            // 1. 计算质心
            var centroid = new Point3D(
                points.Average(p => p.X),
                points.Average(p => p.Y),
                points.Average(p => p.Z)
            );
            
            LogManager.Debug($"[空轨] 质心: ({centroid.X:F2}, {centroid.Y:F2}, {centroid.Z:F2})");

            // 2. 计算协方差矩阵
            double covXX = 0, covYY = 0, covZZ = 0;
            double covXY = 0, covXZ = 0, covYZ = 0;

            foreach (var point in points)
            {
                double dx = point.X - centroid.X;
                double dy = point.Y - centroid.Y;
                double dz = point.Z - centroid.Z;

                covXX += dx * dx;
                covYY += dy * dy;
                covZZ += dz * dz;
                covXY += dx * dy;
                covXZ += dx * dz;
                covYZ += dy * dz;
            }

            covXX /= points.Count;
            covYY /= points.Count;
            covZZ /= points.Count;
            covXY /= points.Count;
            covXZ /= points.Count;
            covYZ /= points.Count;

            // 3. 计算特征值和特征向量
            var eigenvalues = new double[3];
            var eigenvectors = new Point3D[3];
            ComputeEigen3x3(covXX, covYY, covZZ, covXY, covXZ, covYZ, eigenvalues, eigenvectors);

            // 4. 按特征值降序排列（确保 Axis 0 是最长轴）
            var sortedIndices = new[] { 0, 1, 2 };
            Array.Sort(sortedIndices, (i, j) => eigenvalues[j].CompareTo(eigenvalues[i]));

            var sortedEigenvalues = new double[3];
            var sortedEigenvectors = new Point3D[3];
            for (int i = 0; i < 3; i++)
            {
                sortedEigenvalues[i] = eigenvalues[sortedIndices[i]];
                sortedEigenvectors[i] = eigenvectors[sortedIndices[i]];
            }

            LogManager.Debug($"[空轨] OBB特征值（降序）: {sortedEigenvalues[0]:F4}, {sortedEigenvalues[1]:F4}, {sortedEigenvalues[2]:F4}");

            // 5. 将点转换到主轴坐标系
            var axes = sortedEigenvectors;
            var projectedPoints = new List<Point3D>();
            foreach (var point in points)
            {
                var dx = point.X - centroid.X;
                var dy = point.Y - centroid.Y;
                var dz = point.Z - centroid.Z;

                var projected = new Point3D(
                    dx * axes[0].X + dy * axes[0].Y + dz * axes[0].Z,
                    dx * axes[1].X + dy * axes[1].Y + dz * axes[1].Z,
                    dx * axes[2].X + dy * axes[2].Y + dz * axes[2].Z
                );
                projectedPoints.Add(projected);
            }

            // 6. 计算 AABB（在主轴坐标系中）
            var minX = projectedPoints.Min(p => p.X);
            var maxX = projectedPoints.Max(p => p.X);
            var minY = projectedPoints.Min(p => p.Y);
            var maxY = projectedPoints.Max(p => p.Y);
            var minZ = projectedPoints.Min(p => p.Z);
            var maxZ = projectedPoints.Max(p => p.Z);

            LogManager.Debug($"[空轨] 投影范围: Axis0=[{minX:F2}, {maxX:F2}], Axis1=[{minY:F2}, {maxY:F2}], Axis2=[{minZ:F2}, {maxZ:F2}]");

            // 7. 创建 OBB（使用投影范围的中点作为中心，而不是质心）
            var obbCenter = new Point3D(
                centroid.X + axes[0].X * (minX + maxX) / 2 + axes[1].X * (minY + maxY) / 2 + axes[2].X * (minZ + maxZ) / 2,
                centroid.Y + axes[0].Y * (minX + maxX) / 2 + axes[1].Y * (minY + maxY) / 2 + axes[2].Y * (minZ + maxZ) / 2,
                centroid.Z + axes[0].Z * (minX + maxX) / 2 + axes[1].Z * (minY + maxY) / 2 + axes[2].Z * (minZ + maxZ) / 2
            );


            var obb = new OrientedBoundingBox
            {

                Center = obbCenter,
                Axes = axes,
                Extents = new Point3D(
                    (maxX - minX) / 2,
                    (maxY - minY) / 2,
                    (maxZ - minZ) / 2
                ),
                // 保存投影范围信息，用于计算下表面
                ProjectedMinZ = minZ,
                ProjectedMaxZ = maxZ

            };

            LogManager.Debug($"[空轨] OBB各轴长度（模型单位）: {obb.GetAxisLength(0):F2}, {obb.GetAxisLength(1):F2}, {obb.GetAxisLength(2):F2}");
            
            // 输出 OBB 中心和轴方向
            LogManager.Debug($"[空轨] OBB中心: ({obb.Center.X:F2}, {obb.Center.Y:F2}, {obb.Center.Z:F2})");
            for (int i = 0; i < 3; i++)
            {
                var axis = obb.GetAxis(i);
                var axisVector = axis.ToVector3D();
                LogManager.Debug($"[空轨] Axis {i} 方向: ({axisVector.X:F3}, {axisVector.Y:F3}, {axisVector.Z:F3}), 长度: {obb.GetAxisLength(i):F2}");
            }

            return obb;
        }

        /// <summary>
        /// 沿直线采样
        /// </summary>
        private static List<Point3D> SampleLine(Point3D start, Point3D end, double interval)
        {
            var result = new List<Point3D>();
            
            var direction = new Vector3D(end.X - start.X, end.Y - start.Y, end.Z - start.Z);
            var length = Math.Sqrt(direction.X * direction.X + direction.Y * direction.Y + direction.Z * direction.Z);
            
            if (length < 0.001)
            {
                result.Add(start);
                return result;
            }

            // 归一化方向
            direction = new Vector3D(
                direction.X / length,
                direction.Y / length,
                direction.Z / length
            );

            // 计算采样点数
            int numPoints = (int)Math.Ceiling(length / interval) + 1;

            for (int i = 0; i < numPoints; i++)
            {
                var t = (double)i / (numPoints - 1);
                var point = new Point3D(
                    start.X + direction.X * length * t,
                    start.Y + direction.Y * length * t,
                    start.Z + direction.Z * length * t
                );
                result.Add(point);
            }

            return result;
        }

        private static int GetMostVerticalAxisIndex(OrientedBoundingBox obb)
        {
            double bestAbsDot = double.MinValue;
            int bestIndex = 2;

            for (int i = 1; i < 3; i++)
            {
                var axis = obb.GetAxis(i);
                double absDot = Math.Abs(axis.Z);
                if (absDot > bestAbsDot)
                {
                    bestAbsDot = absDot;
                    bestIndex = i;
                }
            }

            return bestIndex;
        }

        private static Point3D ProjectPointToObbLocal(Point3D point, OrientedBoundingBox obb)
        {
            double dx = point.X - obb.Center.X;
            double dy = point.Y - obb.Center.Y;
            double dz = point.Z - obb.Center.Z;

            return new Point3D(
                dx * obb.Axes[0].X + dy * obb.Axes[0].Y + dz * obb.Axes[0].Z,
                dx * obb.Axes[1].X + dy * obb.Axes[1].Y + dz * obb.Axes[1].Z,
                dx * obb.Axes[2].X + dy * obb.Axes[2].Y + dz * obb.Axes[2].Z);
        }

        private static List<Point3D> ExtractDominantRailBand(List<Point3D> localPoints, int verticalAxisIndex)
        {
            const int BinCount = 24;

            double minValue = localPoints.Min(point => GetAxisValue(point, verticalAxisIndex));
            double maxValue = localPoints.Max(point => GetAxisValue(point, verticalAxisIndex));
            double range = maxValue - minValue;
            if (range < 1e-6)
            {
                return new List<Point3D>(localPoints);
            }

            double binSize = range / BinCount;
            var bins = new List<Point3D>[BinCount];
            for (int i = 0; i < BinCount; i++)
            {
                bins[i] = new List<Point3D>();
            }

            foreach (var point in localPoints)
            {
                double value = GetAxisValue(point, verticalAxisIndex);
                int binIndex = (int)((value - minValue) / binSize);
                binIndex = Math.Max(0, Math.Min(BinCount - 1, binIndex));
                bins[binIndex].Add(point);
            }

            int dominantBin = 0;
            int dominantCount = bins[0].Count;
            for (int i = 1; i < bins.Length; i++)
            {
                if (bins[i].Count > dominantCount)
                {
                    dominantBin = i;
                    dominantCount = bins[i].Count;
                }
            }

            var dominantPoints = new List<Point3D>();
            int startBin = Math.Max(0, dominantBin - 1);
            int endBin = Math.Min(BinCount - 1, dominantBin + 1);
            for (int i = startBin; i <= endBin; i++)
            {
                dominantPoints.AddRange(bins[i]);
            }

            return dominantPoints.Count > 0 ? dominantPoints : localPoints;
        }

        private static double EstimateDualRailAcrossCenter(List<Point3D> railBandPoints, int acrossAxisIndex, double acrossAxisLength)
        {
            var acrossValues = railBandPoints
                .Select(point => GetAxisValue(point, acrossAxisIndex))
                .OrderBy(value => value)
                .ToList();

            if (acrossValues.Count < 2)
            {
                return acrossValues.Count == 1 ? acrossValues[0] : 0.0;
            }

            double minAcross = acrossValues.First();
            double maxAcross = acrossValues.Last();
            double totalWidth = maxAcross - minAcross;
            if (totalWidth < Math.Max(1e-6, acrossAxisLength * 0.05))
            {
                return (minAcross + maxAcross) / 2.0;
            }

            double centerA = Percentile(acrossValues, 0.25);
            double centerB = Percentile(acrossValues, 0.75);

            for (int iteration = 0; iteration < 8; iteration++)
            {
                double sumA = 0.0;
                double sumB = 0.0;
                int countA = 0;
                int countB = 0;

                foreach (double value in acrossValues)
                {
                    if (Math.Abs(value - centerA) <= Math.Abs(value - centerB))
                    {
                        sumA += value;
                        countA++;
                    }
                    else
                    {
                        sumB += value;
                        countB++;
                    }
                }

                if (countA == 0 || countB == 0)
                {
                    break;
                }

                centerA = sumA / countA;
                centerB = sumB / countB;
            }

            if (Math.Abs(centerA - centerB) < acrossAxisLength * 0.1)
            {
                return Median(acrossValues);
            }

            return (centerA + centerB) / 2.0;
        }

        private static double GetAxisValue(Point3D point, int axisIndex)
        {
            switch (axisIndex)
            {
                case 0:
                    return point.X;
                case 1:
                    return point.Y;
                case 2:
                    return point.Z;
                default:
                    throw new ArgumentOutOfRangeException(nameof(axisIndex));
            }
        }

        private static double Percentile(List<double> sortedValues, double percentile)
        {
            if (sortedValues == null || sortedValues.Count == 0)
            {
                return 0.0;
            }

            if (sortedValues.Count == 1)
            {
                return sortedValues[0];
            }

            double clamped = Math.Max(0.0, Math.Min(1.0, percentile));
            double position = clamped * (sortedValues.Count - 1);
            int lowerIndex = (int)Math.Floor(position);
            int upperIndex = (int)Math.Ceiling(position);

            if (lowerIndex == upperIndex)
            {
                return sortedValues[lowerIndex];
            }

            double t = position - lowerIndex;
            return sortedValues[lowerIndex] + (sortedValues[upperIndex] - sortedValues[lowerIndex]) * t;
        }

        private static double Median(List<double> sortedValues)
        {
            return Percentile(sortedValues, 0.5);
        }

        /// <summary>
        /// 筛选下表面三角形（法线向下，Z < 0）
        /// </summary>
        private static List<Triangle3D> FilterBottomTriangles(List<Triangle3D> allTriangles)
        {
            var bottomTriangles = new List<Triangle3D>();
            const double Z_THRESHOLD = -0.1; // 法线Z分量阈值（容差约5.7度）

            foreach (var triangle in allTriangles)
            {
                try
                {
                    // 计算三角形法线
                    var normal = CalculateTriangleNormal(triangle);

                    // 判断是否为下表面（法线向下）
                    if (normal.Z < Z_THRESHOLD)
                    {
                        bottomTriangles.Add(triangle);
                    }
                }
                catch (Exception ex)
                {
                    LogManager.Warning($"[空轨] 计算三角形法线失败: {ex.Message}");
                }
            }

            return bottomTriangles;
        }

        /// <summary>
        /// 计算三角形法线
        /// </summary>
        private static Point3D CalculateTriangleNormal(Triangle3D triangle)
        {
            // 计算两条边向量
            var edge1 = new Point3D(
                triangle.Point2.X - triangle.Point1.X,
                triangle.Point2.Y - triangle.Point1.Y,
                triangle.Point2.Z - triangle.Point1.Z);

            var edge2 = new Point3D(
                triangle.Point3.X - triangle.Point1.X,
                triangle.Point3.Y - triangle.Point1.Y,
                triangle.Point3.Z - triangle.Point1.Z);

            // 计算叉积（法线）
            var normal = new Point3D(
                edge1.Y * edge2.Z - edge1.Z * edge2.Y,
                edge1.Z * edge2.X - edge1.X * edge2.Z,
                edge1.X * edge2.Y - edge1.Y * edge2.X);

            // 归一化
            double length = Math.Sqrt(normal.X * normal.X + normal.Y * normal.Y + normal.Z * normal.Z);
            if (length > 0.000001)
            {
                normal = new Point3D(normal.X / length, normal.Y / length, normal.Z / length);
            }

            return normal;
        }

        /// <summary>
        /// 计算3x3对称矩阵的特征值和特征向量
        /// </summary>
        private static void ComputeEigen3x3(double xx, double yy, double zz, 
                                          double xy, double xz, double yz,
                                          double[] eigenvalues, Point3D[] eigenvectors)
        {
            // 使用雅可比迭代法计算特征值和特征向量
            // 初始化为单位矩阵
            double[,] V = new double[3, 3] { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };
            double[,] A = new double[3, 3] { { xx, xy, xz }, { xy, yy, yz }, { xz, yz, zz } };

            const int maxIterations = 100;
            const double tolerance = 1e-10;

            for (int iter = 0; iter < maxIterations; iter++)
            {
                // 找到最大的非对角元素
                int p = 0, q = 1;
                double maxOffDiag = Math.Abs(A[0, 1]);

                if (Math.Abs(A[0, 2]) > maxOffDiag)
                {
                    maxOffDiag = Math.Abs(A[0, 2]);
                    p = 0; q = 2;
                }
                if (Math.Abs(A[1, 2]) > maxOffDiag)
                {
                    maxOffDiag = Math.Abs(A[1, 2]);
                    p = 1; q = 2;
                }

                if (maxOffDiag < tolerance)
                    break;

                // 计算旋转角度
                double theta;
                if (A[p, p] == A[q, q])
                {
                    theta = Math.PI / 4;
                }
                else
                {
                    theta = 0.5 * Math.Atan2(2 * A[p, q], A[q, q] - A[p, p]);
                }

                double c = Math.Cos(theta);
                double s = Math.Sin(theta);

                // 更新矩阵A
                for (int i = 0; i < 3; i++)
                {
                    if (i != p && i != q)
                    {
                        double Api = A[p, i];
                        double Aqi = A[q, i];
                        A[p, i] = c * Api - s * Aqi;
                        A[q, i] = s * Api + c * Aqi;
                        A[i, p] = A[p, i];
                        A[i, q] = A[q, i];
                    }
                }

                double App = A[p, p];
                double Aqq = A[q, q];
                double Apq = A[p, q];

                A[p, p] = c * c * App - 2 * s * c * Apq + s * s * Aqq;
                A[q, q] = s * s * App + 2 * s * c * Apq + c * c * Aqq;
                A[p, q] = 0;
                A[q, p] = 0;

                // 更新特征向量矩阵V
                for (int i = 0; i < 3; i++)
                {
                    double Vip = V[i, p];
                    double Viq = V[i, q];
                    V[i, p] = c * Vip - s * Viq;
                    V[i, q] = s * Vip + c * Viq;
                }
            }

            // 特征值是对角元素
            eigenvalues[0] = A[0, 0];
            eigenvalues[1] = A[1, 1];
            eigenvalues[2] = A[2, 2];

            // 特征向量是V的列
            eigenvectors[0] = new Point3D(V[0, 0], V[1, 0], V[2, 0]);
            eigenvectors[1] = new Point3D(V[0, 1], V[1, 1], V[2, 1]);
            eigenvectors[2] = new Point3D(V[0, 2], V[1, 2], V[2, 2]);
        }

        /// <summary>
        /// 在路径上插值（支持拐弯）
        /// </summary>
        private static Point3D InterpolateOnPath(List<Point3D> path, double progress)
        {
            if (path.Count == 0)
                throw new ArgumentException("路径不能为空");

            if (path.Count == 1)
                return path[0];

            if (progress <= 0) return path[0];
            if (progress >= 1) return path.Last();

            // 计算路径总长度
            double totalLength = CalculatePathLength(path);
            double targetDistance = totalLength * progress;

            // 找到目标距离所在的线段
            double accumulatedLength = 0;
            for (int i = 0; i < path.Count - 1; i++)
            {
                double dx = path[i + 1].X - path[i].X;
                double dy = path[i + 1].Y - path[i].Y;
                double dz = path[i + 1].Z - path[i].Z;
                double segmentLength = Math.Sqrt(dx * dx + dy * dy + dz * dz);

                if (accumulatedLength + segmentLength >= targetDistance)
                {
                    // 在当前线段内插值
                    double segmentProgress = (targetDistance - accumulatedLength) / segmentLength;
                    return new Point3D(
                        path[i].X + segmentProgress * (path[i + 1].X - path[i].X),
                        path[i].Y + segmentProgress * (path[i + 1].Y - path[i].Y),
                        path[i].Z + segmentProgress * (path[i + 1].Z - path[i].Z)
                    );
                }

                accumulatedLength += segmentLength;
            }

            // 如果超出范围，返回最后一个点
            return path.Last();
        }

        /// <summary>
        /// 计算路径总长度
        /// </summary>
        private static double CalculatePathLength(List<Point3D> path)
        {
            if (path.Count < 2) return 0;

            double totalLength = 0;
            for (int i = 0; i < path.Count - 1; i++)
            {
                double dx = path[i + 1].X - path[i].X;
                double dy = path[i + 1].Y - path[i].Y;
                double dz = path[i + 1].Z - path[i].Z;
                totalLength += Math.Sqrt(dx * dx + dy * dy + dz * dz);
            }

            return totalLength;
        }

        /// <summary>
        /// 计算两点之间的距离（2D，忽略Z）
        /// </summary>
        private static double CalculateDistance2D(Point3D p1, Point3D p2)
        {
            double dx = p2.X - p1.X;
            double dy = p2.Y - p1.Y;
            return Math.Sqrt(dx * dx + dy * dy);
        }

        /// <summary>
        /// 从下方射线投射获取下表面高度
        /// </summary>
        private static double GetBottomHeightByRaycast(
            Point3D position, 
            List<Triangle3D> bottomTriangles,
            double minZ)
        {
            try
            {
                // 创建垂直向上的射线（从下方足够远开始）
                var rayOrigin = new Point3D(
                    position.X, 
                    position.Y, 
                    minZ - 1000.0); // 从最低点下方1km开始

                var rayDirection = new Point3D(0, 0, 1); // 向上

                // 执行射线与三角形的交点检测
                var intersectionPoints = new List<double>();

                foreach (var triangle in bottomTriangles)
                {
                    if (GeometryHelper.RayTriangleIntersect(rayOrigin, rayDirection, triangle, out double intersectionZ))
                    {
                        intersectionPoints.Add(intersectionZ);
                    }
                }

                if (intersectionPoints.Count > 0)
                {
                    // 选择最高的交点作为下表面高度
                    return intersectionPoints.Max();
                }
                else
                {
                    // 没有交点，返回无效值
                    return double.MinValue;
                }
            }
            catch (Exception ex)
            {
                LogManager.Error($"[空轨] 射线投射失败: {ex.Message}");
                return double.MinValue;
            }
        }
    }
}
