把动画改成步进式,在动画生成阶段进行快速碰撞计算。
This commit is contained in:
parent
0b0028c19c
commit
cc8842dcd8
368
doc/working/2017/步进式动画方案.md
Normal file
368
doc/working/2017/步进式动画方案.md
Normal file
@ -0,0 +1,368 @@
|
||||
步进式动画系统完整方案 │
|
||||
│ │
|
||||
│ 核心架构 │
|
||||
│ │
|
||||
│ - 步进式播放:基于帧索引而非时间 │
|
||||
│ - 预计算碰撞:提前计算所有碰撞点 │
|
||||
│ - 逐帧控制:支持单帧前进/后退 │
|
||||
│ - 双向导航:可以在任意帧之间跳转 │
|
||||
│ │
|
||||
│ 实现方案 │
|
||||
│ │
|
||||
│ 1. 步进式动画数据结构 │
|
||||
│ │
|
||||
│ // 动画帧数据 │
|
||||
│ public class AnimationFrame │
|
||||
│ { │
|
||||
│ public int FrameIndex { get; set; } │
|
||||
│ public double Progress { get; set; } // 0-1之间的进度 │
|
||||
│ public Point3D Position { get; set; } // 该帧的位置 │
|
||||
│ public List<CollisionInfo> Collisions { get; set; } // 该帧的碰撞信息 │
|
||||
│ public DateTime Timestamp { get; set; } // 帧时间戳(用于性能分析) │
|
||||
│ } │
|
||||
│ │
|
||||
│ public class CollisionInfo │
|
||||
│ { │
|
||||
│ public ModelItem CollidingObject { get; set; } │
|
||||
│ public Point3D CollidingPosition { get; set; } │
|
||||
│ public double Distance { get; set; } │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 在PathAnimationManager中添加 │
|
||||
│ private List<AnimationFrame> _animationFrames; // 所有动画帧 │
|
||||
│ private int _currentFrameIndex = 0; // 当前帧索引 │
|
||||
│ private int _totalFrames = 0; // 总帧数 │
|
||||
│ private double _frameStepSize = 0.01; // 帧步进大小(1%) │
|
||||
│ private bool _isSteppingMode = true; // 是否步进模式 │
|
||||
│ │
|
||||
│ 2. 预计算所有帧(SetupAnimation) │
|
||||
│ │
|
||||
│ public void SetupAnimation(ModelItem animatedObject, List<Point3D> pathPoints, double durationSeconds = 10.0) │
|
||||
│ { │
|
||||
│ // ... 现有验证代码 ... │
|
||||
│ │
|
||||
│ // 计算总帧数 │
|
||||
│ _totalFrames = (int)(1.0 / _frameStepSize); // 如0.01步进=100帧 │
|
||||
│ _animationFrames = new List<AnimationFrame>(); │
|
||||
│ │
|
||||
│ LogManager.Info($"=== 开始预计算动画帧 ==="); │
|
||||
│ LogManager.Info($"总帧数: {_totalFrames}, 步进大小: {_frameStepSize:F3}"); │
|
||||
│ │
|
||||
│ PrecomputeAllFrames(); │
|
||||
│ │
|
||||
│ LogManager.Info($"=== 预计算完成 ==="); │
|
||||
│ LogManager.Info($"总帧数: {_animationFrames.Count}"); │
|
||||
│ LogManager.Info($"包含碰撞的帧: {_animationFrames.Count(f => f.Collisions.Count > 0)}"); │
|
||||
│ │
|
||||
│ // 移动到起点 │
|
||||
│ MoveToFrame(0); │
|
||||
│ } │
|
||||
│ │
|
||||
│ private void PrecomputeAllFrames() │
|
||||
│ { │
|
||||
│ var modelItems = new ModelItemCollection { _animatedObject }; │
|
||||
│ var originalPosition = _currentPosition; │
|
||||
│ │
|
||||
│ // 初始化碰撞检测 │
|
||||
│ ClashDetectiveIntegration.Instance.Initialize(); │
|
||||
│ │
|
||||
│ for (int i = 0; i <= _totalFrames; i++) │
|
||||
│ { │
|
||||
│ double progress = (double)i / _totalFrames; │
|
||||
│ var framePosition = InterpolatePosition(progress); │
|
||||
│ │
|
||||
│ // 临时移动到该帧位置 │
|
||||
│ MoveObjectToPosition(framePosition); │
|
||||
│ │
|
||||
│ // 执行碰撞检测 │
|
||||
│ var collisions = ClashDetectiveIntegration.Instance.DetectCollisions( │
|
||||
│ _animatedObject, null, _detectionGap); │
|
||||
│ │
|
||||
│ // 创建帧数据 │
|
||||
│ var frame = new AnimationFrame │
|
||||
│ { │
|
||||
│ FrameIndex = i, │
|
||||
│ Progress = progress, │
|
||||
│ Position = framePosition, │
|
||||
│ Collisions = new List<CollisionInfo>(), │
|
||||
│ Timestamp = DateTime.Now │
|
||||
│ }; │
|
||||
│ │
|
||||
│ // 记录碰撞信息 │
|
||||
│ foreach (var collision in collisions) │
|
||||
│ { │
|
||||
│ frame.Collisions.Add(new CollisionInfo │
|
||||
│ { │
|
||||
│ CollidingObject = collision.Item2, │
|
||||
│ CollidingPosition = GetObjectPosition(collision.Item2), │
|
||||
│ Distance = collision.Distance │
|
||||
│ }); │
|
||||
│ } │
|
||||
│ │
|
||||
│ _animationFrames.Add(frame); │
|
||||
│ │
|
||||
│ // 进度报告 │
|
||||
│ if (i % 10 == 0) │
|
||||
│ { │
|
||||
│ LogManager.Info($"预计算进度: {i}/{_totalFrames} ({progress*100:F1}%)"); │
|
||||
│ } │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 恢复原始位置 │
|
||||
│ MoveObjectToPosition(originalPosition); │
|
||||
│ } │
|
||||
│ │
|
||||
│ 3. 步进式播放控制 │
|
||||
│ │
|
||||
│ // 播放模式枚举 │
|
||||
│ public enum PlaybackMode │
|
||||
│ { │
|
||||
│ Continuous, // 连续播放 │
|
||||
│ StepByStep, // 逐帧步进 │
|
||||
│ Manual // 手动控制 │
|
||||
│ } │
|
||||
│ │
|
||||
│ private PlaybackMode _playbackMode = PlaybackMode.StepByStep; │
|
||||
│ │
|
||||
│ // 修改OnApplicationIdle - 支持步进模式 │
|
||||
│ private void OnApplicationIdle(object sender, EventArgs e) │
|
||||
│ { │
|
||||
│ if (_currentState != AnimationState.Playing) │
|
||||
│ return; │
|
||||
│ │
|
||||
│ // 帧率控制 │
|
||||
│ var now = DateTime.Now; │
|
||||
│ var elapsed = (now - _lastFrameTime).TotalMilliseconds; │
|
||||
│ │
|
||||
│ if (elapsed < _frameInterval) │
|
||||
│ return; │
|
||||
│ │
|
||||
│ // 根据播放模式执行 │
|
||||
│ switch (_playbackMode) │
|
||||
│ { │
|
||||
│ case PlaybackMode.Continuous: │
|
||||
│ // 自动步进到下一帧 │
|
||||
│ if (_currentFrameIndex < _totalFrames) │
|
||||
│ { │
|
||||
│ MoveToFrame(_currentFrameIndex + 1); │
|
||||
│ _lastFrameTime = now; │
|
||||
│ } │
|
||||
│ else │
|
||||
│ { │
|
||||
│ FinishAnimation(); │
|
||||
│ } │
|
||||
│ break; │
|
||||
│ │
|
||||
│ case PlaybackMode.StepByStep: │
|
||||
│ // 等待用户触发下一帧 │
|
||||
│ // 不自动前进 │
|
||||
│ break; │
|
||||
│ │
|
||||
│ case PlaybackMode.Manual: │
|
||||
│ // 完全手动控制 │
|
||||
│ break; │
|
||||
│ } │
|
||||
│ } │
|
||||
│ │
|
||||
│ 4. 帧导航方法 │
|
||||
│ │
|
||||
│ // 移动到指定帧 │
|
||||
│ public void MoveToFrame(int frameIndex) │
|
||||
│ { │
|
||||
│ if (frameIndex < 0 || frameIndex >= _animationFrames.Count) │
|
||||
│ { │
|
||||
│ LogManager.Warning($"帧索引 {frameIndex} 超出范围 [0, {_animationFrames.Count-1}]"); │
|
||||
│ return; │
|
||||
│ } │
|
||||
│ │
|
||||
│ var frame = _animationFrames[frameIndex]; │
|
||||
│ _currentFrameIndex = frameIndex; │
|
||||
│ │
|
||||
│ // 更新位置 │
|
||||
│ UpdateObjectPosition(frame.Position); │
|
||||
│ _currentPosition = frame.Position; │
|
||||
│ │
|
||||
│ // 更新碰撞高亮 │
|
||||
│ HighlightFrameCollisions(frame); │
|
||||
│ │
|
||||
│ // 触发进度事件 │
|
||||
│ ProgressChanged?.Invoke(this, frame.Progress * 100); │
|
||||
│ │
|
||||
│ // 更新TimeLiner │
|
||||
│ if (_timeLinerManager != null && !string.IsNullOrEmpty(_currentTaskId)) │
|
||||
│ { │
|
||||
│ _timeLinerManager.UpdateTaskProgress(_currentTaskId, frame.Progress, _currentState); │
|
||||
│ } │
|
||||
│ │
|
||||
│ LogManager.Debug($"移动到帧 {frameIndex}/{_totalFrames} (进度: {frame.Progress*100:F1}%)"); │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 前进一帧 │
|
||||
│ public void StepForward() │
|
||||
│ { │
|
||||
│ if (_currentFrameIndex < _totalFrames - 1) │
|
||||
│ { │
|
||||
│ MoveToFrame(_currentFrameIndex + 1); │
|
||||
│ } │
|
||||
│ else │
|
||||
│ { │
|
||||
│ LogManager.Info("已到达最后一帧"); │
|
||||
│ } │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 后退一帧 │
|
||||
│ public void StepBackward() │
|
||||
│ { │
|
||||
│ if (_currentFrameIndex > 0) │
|
||||
│ { │
|
||||
│ MoveToFrame(_currentFrameIndex - 1); │
|
||||
│ } │
|
||||
│ else │
|
||||
│ { │
|
||||
│ LogManager.Info("已到达第一帧"); │
|
||||
│ } │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 跳转到指定进度 │
|
||||
│ public void JumpToProgress(double progress) │
|
||||
│ { │
|
||||
│ int targetFrame = (int)(progress * _totalFrames); │
|
||||
│ MoveToFrame(targetFrame); │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 快进/快退 │
|
||||
│ public void FastForward(int frames = 10) │
|
||||
│ { │
|
||||
│ int targetFrame = Math.Min(_currentFrameIndex + frames, _totalFrames - 1); │
|
||||
│ MoveToFrame(targetFrame); │
|
||||
│ } │
|
||||
│ │
|
||||
│ public void FastBackward(int frames = 10) │
|
||||
│ { │
|
||||
│ int targetFrame = Math.Max(_currentFrameIndex - frames, 0); │
|
||||
│ MoveToFrame(targetFrame); │
|
||||
│ } │
|
||||
│ │
|
||||
│ 5. 碰撞高亮管理 │
|
||||
│ │
|
||||
│ private HashSet<ModelItem> _currentHighlightedItems = new HashSet<ModelItem>(); │
|
||||
│ │
|
||||
│ private void HighlightFrameCollisions(AnimationFrame frame) │
|
||||
│ { │
|
||||
│ var newHighlights = new HashSet<ModelItem>(frame.Collisions.Select(c => c.CollidingObject)); │
|
||||
│ │
|
||||
│ // 只在高亮集合变化时更新 │
|
||||
│ if (!newHighlights.SetEquals(_currentHighlightedItems)) │
|
||||
│ { │
|
||||
│ // 清除旧高亮 │
|
||||
│ if (_currentHighlightedItems.Count > 0) │
|
||||
│ { │
|
||||
│ ClashDetectiveIntegration.Instance.ClearHighlight(); │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 应用新高亮 │
|
||||
│ if (newHighlights.Count > 0) │
|
||||
│ { │
|
||||
│ var collisionResults = frame.Collisions.Select(c => new CollisionResult │
|
||||
│ { │
|
||||
│ Item1 = _animatedObject, │
|
||||
│ Item2 = c.CollidingObject, │
|
||||
│ Distance = c.Distance │
|
||||
│ }).ToList(); │
|
||||
│ │
|
||||
│ ClashDetectiveIntegration.Instance.HighlightCollisions(collisionResults); │
|
||||
│ } │
|
||||
│ │
|
||||
│ _currentHighlightedItems = newHighlights; │
|
||||
│ │
|
||||
│ if (frame.Collisions.Count > 0) │
|
||||
│ { │
|
||||
│ LogManager.Info($"帧 {frame.FrameIndex}: 高亮 {frame.Collisions.Count} 个碰撞"); │
|
||||
│ } │
|
||||
│ } │
|
||||
│ } │
|
||||
│ │
|
||||
│ 6. 播放控制API │
|
||||
│ │
|
||||
│ // 开始连续播放 │
|
||||
│ public void Play() │
|
||||
│ { │
|
||||
│ _playbackMode = PlaybackMode.Continuous; │
|
||||
│ SetState(AnimationState.Playing); │
|
||||
│ NavisApplication.Idle += OnApplicationIdle; │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 暂停(保持当前帧) │
|
||||
│ public void Pause() │
|
||||
│ { │
|
||||
│ _playbackMode = PlaybackMode.StepByStep; │
|
||||
│ SetState(AnimationState.Paused); │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 设置播放速度(通过调整帧间隔) │
|
||||
│ public void SetPlaybackSpeed(double speed) │
|
||||
│ { │
|
||||
│ _frameInterval = (1000.0 / _animationFrameRate) / speed; │
|
||||
│ LogManager.Info($"播放速度设置为 {speed}x"); │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 获取当前帧信息 │
|
||||
│ public AnimationFrame GetCurrentFrame() │
|
||||
│ { │
|
||||
│ if (_currentFrameIndex >= 0 && _currentFrameIndex < _animationFrames.Count) │
|
||||
│ return _animationFrames[_currentFrameIndex]; │
|
||||
│ return null; │
|
||||
│ } │
|
||||
│ │
|
||||
│ // 获取帧统计信息 │
|
||||
│ public string GetFrameStatistics() │
|
||||
│ { │
|
||||
│ var current = GetCurrentFrame(); │
|
||||
│ if (current != null) │
|
||||
│ { │
|
||||
│ return $"帧 {_currentFrameIndex}/{_totalFrames} | " + │
|
||||
│ $"进度 {current.Progress*100:F1}% | " + │
|
||||
│ $"碰撞 {current.Collisions.Count}"; │
|
||||
│ } │
|
||||
│ return "无帧数据"; │
|
||||
│ } │
|
||||
│ │
|
||||
│ 7. UI集成建议 │
|
||||
│ │
|
||||
│ // 动画控制面板应添加的控件 │
|
||||
│ public class AnimationControlPanel │
|
||||
│ { │
|
||||
│ // 播放控制 │
|
||||
│ Button PlayButton; // 连续播放 │
|
||||
│ Button PauseButton; // 暂停 │
|
||||
│ Button StepForwardButton; // 前进一帧 │
|
||||
│ Button StepBackButton; // 后退一帧 │
|
||||
│ Button FastForwardButton; // 快进10帧 │
|
||||
│ Button FastRewindButton; // 快退10帧 │
|
||||
│ │
|
||||
│ // 进度控制 │
|
||||
│ Slider ProgressSlider; // 进度条(可拖动跳转) │
|
||||
│ Label FrameLabel; // 当前帧信息 │
|
||||
│ Label CollisionLabel; // 碰撞信息 │
|
||||
│ │
|
||||
│ // 速度控制 │
|
||||
│ Slider SpeedSlider; // 播放速度(0.1x - 5x) │
|
||||
│ ComboBox PlayModeCombo; // 播放模式选择 │
|
||||
│ } │
|
||||
│ │
|
||||
│ 优势 │
|
||||
│ │
|
||||
│ 1. 完全可控:每一帧都可以精确控制 │
|
||||
│ 2. 双向导航:支持前进和后退 │
|
||||
│ 3. 性能优秀:预计算避免实时检测 │
|
||||
│ 4. 调试友好:可以逐帧分析碰撞 │
|
||||
│ 5. 灵活播放:支持多种播放模式 │
|
||||
│ │
|
||||
│ 实施步骤 │
|
||||
│ │
|
||||
│ 1. 添加AnimationFrame数据结构 │
|
||||
│ 2. 实现预计算所有帧逻辑 │
|
||||
│ 3. 改造播放控制为步进式 │
|
||||
│ 4. 实现帧导航方法 │
|
||||
│ 5. 更新UI添加步进控制 │
|
||||
│ 6. 测试各种播放模式
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using NavisApplication = Autodesk.Navisworks.Api.Application;
|
||||
|
||||
@ -24,16 +25,37 @@ namespace NavisworksTransport.Core.Animation
|
||||
/// 注意:由于Navisworks API限制,无法直接使用Animator API,因此使用OverridePermanentTransform实现动画
|
||||
/// 已集成 TimeLiner 功能,支持在 TimeLiner 中显示和管理动画任务
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 动画帧数据(包含位置和碰撞信息)
|
||||
/// </summary>
|
||||
public class AnimationFrame
|
||||
{
|
||||
public int Index { get; set; } // 帧索引
|
||||
public double Progress { get; set; } // 进度(0-1)
|
||||
public Point3D Position { get; set; } // 该帧的位置
|
||||
public List<CollisionResult> Collisions { get; set; } // 该帧的碰撞结果
|
||||
public bool HasCollision => Collisions?.Count > 0;
|
||||
|
||||
public AnimationFrame()
|
||||
{
|
||||
Collisions = new List<CollisionResult>();
|
||||
}
|
||||
}
|
||||
|
||||
public class PathAnimationManager
|
||||
{
|
||||
private static PathAnimationManager _instance;
|
||||
private ModelItem _animatedObject;
|
||||
private List<Point3D> _pathPoints;
|
||||
|
||||
// === 预计算动画系统 ===
|
||||
private List<AnimationFrame> _animationFrames; // 所有帧数据
|
||||
private int _currentFrameIndex = 0; // 当前帧索引
|
||||
private HashSet<ModelItem> _currentHighlightedItems; // 当前高亮的对象
|
||||
|
||||
// === Idle事件动画系统 ===
|
||||
private double _frameInterval; // 帧间隔(毫秒)
|
||||
private DateTime _lastFrameTime = DateTime.MinValue; // 上一帧时间
|
||||
private int _collisionCheckCounter = 0; // 碰撞检测计数器(用于降频)
|
||||
|
||||
// === 动画参数 ===
|
||||
private double _animationDuration = 10.0; // 动画总时长(秒)
|
||||
@ -81,6 +103,8 @@ namespace NavisworksTransport.Core.Animation
|
||||
public PathAnimationManager()
|
||||
{
|
||||
_pathPoints = new List<Point3D>();
|
||||
_animationFrames = new List<AnimationFrame>();
|
||||
_currentHighlightedItems = new HashSet<ModelItem>();
|
||||
|
||||
// 初始化动画模式
|
||||
_frameInterval = 1000.0 / _animationFrameRate; // 计算帧间隔
|
||||
@ -173,6 +197,9 @@ namespace NavisworksTransport.Core.Animation
|
||||
var originalBoundingBox = animatedObject.BoundingBox();
|
||||
_originalCenter = originalBoundingBox.Center;
|
||||
|
||||
// 预计算动画帧和碰撞
|
||||
PrecomputeAnimationFrames();
|
||||
|
||||
// 关键修复:将车辆立即移动到路径起点
|
||||
// 这样动画就从路径起点开始,而不是从车辆当前位置开始
|
||||
MoveVehicleToPathStart();
|
||||
@ -247,6 +274,240 @@ namespace NavisworksTransport.Core.Animation
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 预计算所有动画帧和碰撞信息
|
||||
/// </summary>
|
||||
private void PrecomputeAnimationFrames()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 基于动画时长和帧率计算总帧数
|
||||
int totalFrames = (int)(_animationDuration * _animationFrameRate);
|
||||
|
||||
// 反向计算检测精度(确保每帧都检测)
|
||||
var totalDistance = CalculateTotalPathDistance();
|
||||
_collisionDetectionAccuracy = totalDistance / totalFrames;
|
||||
|
||||
LogManager.Info($"=== 预计算动画帧 ===");
|
||||
LogManager.Info($"动画时长: {_animationDuration}秒");
|
||||
LogManager.Info($"动画帧率: {_animationFrameRate} FPS");
|
||||
LogManager.Info($"总帧数: {totalFrames}");
|
||||
LogManager.Info($"路径总长: {totalDistance:F2}米");
|
||||
LogManager.Info($"检测精度: {_collisionDetectionAccuracy:F3}米/帧");
|
||||
|
||||
// 初始化帧列表
|
||||
_animationFrames = new List<AnimationFrame>();
|
||||
_currentHighlightedItems = new HashSet<ModelItem>();
|
||||
_currentFrameIndex = 0;
|
||||
|
||||
// 初始化碰撞检测系统
|
||||
ClashDetectiveIntegration.Instance.Initialize();
|
||||
|
||||
// 获取动画对象的包围盒信息
|
||||
var originalBoundingBox = _animatedObject.BoundingBox();
|
||||
var boundingBoxSize = new Vector3D(
|
||||
originalBoundingBox.Max.X - originalBoundingBox.Min.X,
|
||||
originalBoundingBox.Max.Y - originalBoundingBox.Min.Y,
|
||||
originalBoundingBox.Max.Z - originalBoundingBox.Min.Z
|
||||
);
|
||||
|
||||
// 获取场景中所有可能碰撞的对象
|
||||
var potentialColliders = GetPotentialColliders();
|
||||
LogManager.Info($"潜在碰撞对象数: {potentialColliders.Count}");
|
||||
|
||||
// 预计算每一帧
|
||||
for (int i = 0; i <= totalFrames; i++)
|
||||
{
|
||||
double progress = (double)i / totalFrames;
|
||||
var framePosition = InterpolatePosition(progress);
|
||||
|
||||
// 创建帧数据
|
||||
var frame = new AnimationFrame
|
||||
{
|
||||
Index = i,
|
||||
Progress = progress,
|
||||
Position = framePosition,
|
||||
Collisions = new List<CollisionResult>()
|
||||
};
|
||||
|
||||
// 虚拟碰撞检测(不移动实际物体)
|
||||
var virtualBoundingBox = CreateVirtualBoundingBox(framePosition, boundingBoxSize);
|
||||
|
||||
// 检测与所有潜在对象的碰撞
|
||||
foreach (var collider in potentialColliders)
|
||||
{
|
||||
var colliderBox = collider.BoundingBox();
|
||||
|
||||
// 使用包围盒检测
|
||||
if (BoundingBoxesIntersectWithGap(virtualBoundingBox, colliderBox, _detectionGap))
|
||||
{
|
||||
frame.Collisions.Add(new CollisionResult
|
||||
{
|
||||
Item1 = _animatedObject,
|
||||
Item2 = collider,
|
||||
Distance = CalculateBoundingBoxDistance(virtualBoundingBox, colliderBox)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_animationFrames.Add(frame);
|
||||
|
||||
// 进度报告
|
||||
if (i % 30 == 0) // 每秒报告一次(30fps)
|
||||
{
|
||||
LogManager.Info($"预计算进度: 帧 {i}/{totalFrames} ({progress*100:F1}%)");
|
||||
if (frame.HasCollision)
|
||||
{
|
||||
LogManager.Debug($" 帧 {i} 检测到 {frame.Collisions.Count} 个碰撞");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 统计碰撞信息
|
||||
var framesWithCollision = _animationFrames.Count(f => f.HasCollision);
|
||||
var totalCollisions = _animationFrames.Sum(f => f.Collisions.Count);
|
||||
LogManager.Info($"=== 预计算完成 ===");
|
||||
LogManager.Info($"总帧数: {_animationFrames.Count}");
|
||||
LogManager.Info($"包含碰撞的帧: {framesWithCollision}");
|
||||
LogManager.Info($"总碰撞次数: {totalCollisions}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"预计算动画帧失败: {ex.Message}");
|
||||
// 如果预计算失败,创建空帧列表以避免崩溃
|
||||
_animationFrames = new List<AnimationFrame>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取潜在碰撞对象列表
|
||||
/// </summary>
|
||||
private List<ModelItem> GetPotentialColliders()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Info("开始获取潜在碰撞对象");
|
||||
|
||||
// 确保ClashDetectiveIntegration已初始化并构建了通道缓存
|
||||
ClashDetectiveIntegration.Instance.Initialize();
|
||||
ClashDetectiveIntegration.Instance.BuildChannelObjectsCache();
|
||||
ClashDetectiveIntegration.BuildAllGeometryItemsCache();
|
||||
|
||||
// 获取所有有几何体的对象
|
||||
var allItems = Application.ActiveDocument.Models.RootItemDescendantsAndSelf
|
||||
.Where(item => item.HasGeometry)
|
||||
.ToList();
|
||||
|
||||
// 构建排除列表:动画对象本身及其子对象
|
||||
var excludeList = new ModelItemCollection();
|
||||
excludeList.AddRange(_animatedObject.DescendantsAndSelf);
|
||||
|
||||
// 使用ClashDetectiveIntegration的碰撞检测方法,它会自动排除通道对象
|
||||
// 创建一个较大的检测间隙来获取路径范围内所有潜在碰撞对象
|
||||
var detectionGap = 100.0; // 100米范围内的对象都视为潜在碰撞对象
|
||||
|
||||
// 调用ClashDetectiveIntegration的DetectCollisions方法
|
||||
// 该方法内部会自动排除通道对象(通过IsChannelObject方法)
|
||||
var collisionResults = ClashDetectiveIntegration.Instance.DetectCollisions(
|
||||
_animatedObject,
|
||||
excludeList,
|
||||
detectionGap
|
||||
);
|
||||
|
||||
// 从碰撞结果中提取唯一的碰撞对象
|
||||
var potentialColliders = new HashSet<ModelItem>();
|
||||
foreach (var result in collisionResults)
|
||||
{
|
||||
if (result.Item2 != null && !potentialColliders.Contains(result.Item2))
|
||||
{
|
||||
potentialColliders.Add(result.Item2);
|
||||
}
|
||||
}
|
||||
|
||||
var finalList = potentialColliders.ToList();
|
||||
|
||||
LogManager.Info($"获取潜在碰撞对象完成: 总对象={allItems.Count}, " +
|
||||
$"排除自身及子对象={excludeList.Count}, " +
|
||||
$"检测范围内潜在碰撞对象={finalList.Count}(已自动排除通道)");
|
||||
|
||||
return finalList;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取潜在碰撞对象失败: {ex.Message}");
|
||||
return new List<ModelItem>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算路径的包围盒
|
||||
/// </summary>
|
||||
private BoundingBox3D CalculatePathBounds()
|
||||
{
|
||||
if (_pathPoints == null || _pathPoints.Count == 0)
|
||||
return new BoundingBox3D();
|
||||
|
||||
var minX = _pathPoints.Min(p => p.X);
|
||||
var minY = _pathPoints.Min(p => p.Y);
|
||||
var minZ = _pathPoints.Min(p => p.Z);
|
||||
var maxX = _pathPoints.Max(p => p.X);
|
||||
var maxY = _pathPoints.Max(p => p.Y);
|
||||
var maxZ = _pathPoints.Max(p => p.Z);
|
||||
|
||||
return new BoundingBox3D(
|
||||
new Point3D(minX, minY, minZ),
|
||||
new Point3D(maxX, maxY, maxZ)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建虚拟包围盒(用于碰撞检测)
|
||||
/// </summary>
|
||||
private BoundingBox3D CreateVirtualBoundingBox(Point3D position, Vector3D size)
|
||||
{
|
||||
return new BoundingBox3D(
|
||||
new Point3D(
|
||||
position.X - size.X / 2,
|
||||
position.Y - size.Y / 2,
|
||||
position.Z
|
||||
),
|
||||
new Point3D(
|
||||
position.X + size.X / 2,
|
||||
position.Y + size.Y / 2,
|
||||
position.Z + size.Z
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 包围盒碰撞检测(带间隙)
|
||||
/// </summary>
|
||||
private bool BoundingBoxesIntersectWithGap(BoundingBox3D box1, BoundingBox3D box2, double gap)
|
||||
{
|
||||
return !(box1.Max.X + gap < box2.Min.X || box2.Max.X + gap < box1.Min.X ||
|
||||
box1.Max.Y + gap < box2.Min.Y || box2.Max.Y + gap < box1.Min.Y ||
|
||||
box1.Max.Z + gap < box2.Min.Z || box2.Max.Z + gap < box1.Min.Z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算包围盒中心距离
|
||||
/// </summary>
|
||||
private double CalculateBoundingBoxDistance(BoundingBox3D box1, BoundingBox3D box2)
|
||||
{
|
||||
var center1 = new Point3D(
|
||||
(box1.Min.X + box1.Max.X) / 2,
|
||||
(box1.Min.Y + box1.Max.Y) / 2,
|
||||
(box1.Min.Z + box1.Max.Z) / 2
|
||||
);
|
||||
var center2 = new Point3D(
|
||||
(box2.Min.X + box2.Max.X) / 2,
|
||||
(box2.Min.Y + box2.Max.Y) / 2,
|
||||
(box2.Min.Z + box2.Max.Z) / 2
|
||||
);
|
||||
|
||||
return CalculateDistance(center1, center2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始播放动画
|
||||
/// </summary>
|
||||
@ -309,7 +570,6 @@ namespace NavisworksTransport.Core.Animation
|
||||
|
||||
// 重置Idle模式状态
|
||||
_lastFrameTime = DateTime.MinValue;
|
||||
_collisionCheckCounter = 0;
|
||||
_fpsFrameCount = 0;
|
||||
_fpsCounterStart = DateTime.Now;
|
||||
_frameInterval = 1000.0 / _animationFrameRate;
|
||||
@ -345,8 +605,41 @@ namespace NavisworksTransport.Core.Animation
|
||||
NavisApplication.Idle -= OnApplicationIdle;
|
||||
LogManager.Debug("[Idle模式] Idle事件已注销");
|
||||
|
||||
// 收集整个路径上的所有碰撞对象
|
||||
var allCollisionResults = new List<CollisionResult>();
|
||||
var uniqueCollidedItems = new HashSet<ModelItem>();
|
||||
|
||||
foreach (var frame in _animationFrames)
|
||||
{
|
||||
if (frame.HasCollision)
|
||||
{
|
||||
foreach (var collision in frame.Collisions)
|
||||
{
|
||||
// 收集唯一的碰撞对象
|
||||
if (!uniqueCollidedItems.Contains(collision.Item2))
|
||||
{
|
||||
uniqueCollidedItems.Add(collision.Item2);
|
||||
allCollisionResults.Add(collision);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 高亮显示所有碰撞过的对象
|
||||
if (allCollisionResults.Count > 0)
|
||||
{
|
||||
ClashDetectiveIntegration.Instance.HighlightCollisions(allCollisionResults);
|
||||
LogManager.Info($"动画停止,高亮显示路径上的 {uniqueCollidedItems.Count} 个碰撞对象");
|
||||
}
|
||||
else
|
||||
{
|
||||
ClashDetectiveIntegration.Instance.ClearHighlights();
|
||||
LogManager.Info("动画停止,路径上无碰撞对象");
|
||||
}
|
||||
|
||||
SetState(AnimationState.Stopped);
|
||||
_pausedProgress = 0.0; // 重置暂停进度
|
||||
_currentFrameIndex = 0; // 重置帧索引
|
||||
LogManager.Info("动画已停止");
|
||||
|
||||
// 动画停止时不创建碰撞测试汇总,由动画完成事件统一处理
|
||||
@ -377,6 +670,38 @@ namespace NavisworksTransport.Core.Animation
|
||||
// 重置暂停进度
|
||||
_pausedProgress = 0.0;
|
||||
|
||||
// 收集整个路径上的所有碰撞对象
|
||||
var allCollisionResults = new List<CollisionResult>();
|
||||
var uniqueCollidedItems = new HashSet<ModelItem>();
|
||||
|
||||
foreach (var frame in _animationFrames)
|
||||
{
|
||||
if (frame.HasCollision)
|
||||
{
|
||||
foreach (var collision in frame.Collisions)
|
||||
{
|
||||
// 收集唯一的碰撞对象
|
||||
if (!uniqueCollidedItems.Contains(collision.Item2))
|
||||
{
|
||||
uniqueCollidedItems.Add(collision.Item2);
|
||||
allCollisionResults.Add(collision);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 高亮显示所有碰撞过的对象
|
||||
if (allCollisionResults.Count > 0)
|
||||
{
|
||||
ClashDetectiveIntegration.Instance.HighlightCollisions(allCollisionResults);
|
||||
LogManager.Info($"动画完成,高亮显示路径上的 {uniqueCollidedItems.Count} 个碰撞对象");
|
||||
}
|
||||
else
|
||||
{
|
||||
ClashDetectiveIntegration.Instance.ClearHighlights();
|
||||
LogManager.Info("动画完成,路径上无碰撞对象");
|
||||
}
|
||||
|
||||
// 直接设置为完成状态,避免中间状态切换
|
||||
SetState(AnimationState.Finished);
|
||||
|
||||
@ -1274,6 +1599,60 @@ namespace NavisworksTransport.Core.Animation
|
||||
/// <summary>
|
||||
/// Idle事件处理器 - 新的动画核心
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// 根据当前帧索引更新碰撞高亮
|
||||
/// </summary>
|
||||
private void UpdateCollisionHighlightFromFrame()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保帧索引有效
|
||||
if (_currentFrameIndex < 0 || _currentFrameIndex >= _animationFrames.Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前帧数据
|
||||
var currentFrame = _animationFrames[_currentFrameIndex];
|
||||
|
||||
// 使用ClashDetectiveIntegration的高亮方法
|
||||
if (currentFrame.HasCollision)
|
||||
{
|
||||
// 高亮当前帧的碰撞对象
|
||||
ClashDetectiveIntegration.Instance.HighlightCollisions(currentFrame.Collisions);
|
||||
|
||||
// 缓存碰撞结果用于动画结束后的处理
|
||||
var animatedObjectPosition = currentFrame.Position;
|
||||
foreach (var collision in currentFrame.Collisions)
|
||||
{
|
||||
var collisionObjectPosition = GetObjectPosition(collision.Item2);
|
||||
ClashDetectiveIntegration.Instance.CacheCollisionDuringAnimation(
|
||||
_animatedObject,
|
||||
animatedObjectPosition,
|
||||
collision.Item2,
|
||||
collisionObjectPosition
|
||||
);
|
||||
}
|
||||
|
||||
LogManager.Debug($"帧 {_currentFrameIndex}: 检测到 {currentFrame.Collisions.Count} 个碰撞");
|
||||
|
||||
// 触发碰撞事件
|
||||
var eventArgs = new CollisionDetectedEventArgs(currentFrame.Collisions);
|
||||
OnCollisionDetected(eventArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 清除高亮(没有碰撞时)
|
||||
ClashDetectiveIntegration.Instance.ClearHighlights();
|
||||
LogManager.Debug($"帧 {_currentFrameIndex}: 无碰撞");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"更新碰撞高亮失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationIdle(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
@ -1305,9 +1684,27 @@ namespace NavisworksTransport.Core.Animation
|
||||
var totalElapsed = (now - _animationStartTime).TotalSeconds;
|
||||
var progress = Math.Min(totalElapsed / _animationDuration, 1.0);
|
||||
|
||||
// 更新动画位置(使用原有逻辑)
|
||||
Point3D newPosition = InterpolatePosition(progress);
|
||||
UpdateObjectPosition(newPosition);
|
||||
// 根据进度计算当前帧索引
|
||||
int targetFrameIndex = (int)(progress * (_animationFrames.Count - 1));
|
||||
targetFrameIndex = Math.Min(targetFrameIndex, _animationFrames.Count - 1);
|
||||
|
||||
// 如果帧索引发生变化,更新位置和碰撞高亮
|
||||
if (targetFrameIndex != _currentFrameIndex || _currentFrameIndex == 0)
|
||||
{
|
||||
_currentFrameIndex = targetFrameIndex;
|
||||
|
||||
// 使用预计算的帧位置
|
||||
if (_currentFrameIndex < _animationFrames.Count)
|
||||
{
|
||||
var frameData = _animationFrames[_currentFrameIndex];
|
||||
UpdateObjectPosition(frameData.Position);
|
||||
|
||||
// 更新碰撞高亮(基于预计算结果)
|
||||
UpdateCollisionHighlightFromFrame();
|
||||
|
||||
LogManager.Debug($"帧 {_currentFrameIndex}/{_animationFrames.Count-1}, 进度: {progress*100:F1}%, 位置: ({frameData.Position.X:F2},{frameData.Position.Y:F2},{frameData.Position.Z:F2})");
|
||||
}
|
||||
}
|
||||
|
||||
// 触发进度事件
|
||||
var progressPercent = progress * 100;
|
||||
@ -1319,17 +1716,6 @@ namespace NavisworksTransport.Core.Animation
|
||||
_timeLinerManager.UpdateTaskProgress(_currentTaskId, progress, _currentState);
|
||||
}
|
||||
|
||||
// 碰撞检测(降频执行,每3帧检测一次)
|
||||
_collisionCheckCounter++;
|
||||
if (_collisionCheckCounter >= 3)
|
||||
{
|
||||
_collisionCheckCounter = 0;
|
||||
if (_currentState == AnimationState.Playing)
|
||||
{
|
||||
CheckAndHighlightCollisionsWithClashDetective();
|
||||
}
|
||||
}
|
||||
|
||||
// 记录帧时间
|
||||
_lastFrameTime = now;
|
||||
_animationFrameCount++;
|
||||
|
||||
@ -106,40 +106,6 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 执行动态碰撞检测
|
||||
/// </summary>
|
||||
/// <param name="animatedObject">动画对象</param>
|
||||
/// <param name="excludeObjects">排除对象</param>
|
||||
/// <param name="detectionGap">检测间隙(米)</param>
|
||||
/// <returns>碰撞结果</returns>
|
||||
public List<CollisionResult> DetectCollisions(ModelItem animatedObject,
|
||||
ModelItemCollection excludeObjects = null, double detectionGap = 0.2)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用简化检测模式
|
||||
LogManager.Debug($"使用简化检测模式进行碰撞检测,检测间隙: {detectionGap}米");
|
||||
var results = DetectCollisionsSimple(animatedObject, excludeObjects, detectionGap);
|
||||
|
||||
// 在动画过程中不创建Clash Detective测试,避免影响性能
|
||||
// 只有在动画结束后手动测试时才创建完整的测试
|
||||
|
||||
// 触发事件
|
||||
OnCollisionDetected(new CollisionDetectedEventArgs(results));
|
||||
|
||||
LogManager.Debug($"碰撞检测完成,发现 {results.Count} 个碰撞");
|
||||
return results;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"碰撞检测失败: {ex.Message}");
|
||||
// 回退到简化检测
|
||||
return DetectCollisionsSimple(animatedObject, excludeObjects, detectionGap);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建碰撞快照(动画结束后一次性更新结果)
|
||||
/// </summary>
|
||||
@ -187,34 +153,6 @@ namespace NavisworksTransport
|
||||
{
|
||||
try
|
||||
{
|
||||
// 🔍 添加碰撞缓存开始的详细日志
|
||||
LogManager.Debug($"=== [碰撞缓存-开始] ===");
|
||||
LogManager.Debug($"[碰撞缓存-开始] 动画对象: {animatedObject?.DisplayName ?? "NULL"}");
|
||||
LogManager.Debug($"[碰撞缓存-开始] 碰撞对象: {collisionObject?.DisplayName ?? "NULL"}");
|
||||
|
||||
// 🔍 对象验证详情
|
||||
if (animatedObject == null)
|
||||
{
|
||||
LogManager.Error($"[碰撞缓存-错误] 动画对象为NULL!");
|
||||
}
|
||||
|
||||
if (collisionObject == null)
|
||||
{
|
||||
LogManager.Error($"[碰撞缓存-错误] 碰撞对象为NULL!");
|
||||
}
|
||||
|
||||
// 🔍 位置信息详情
|
||||
LogManager.Debug($"[碰撞缓存-位置] 动画对象位置: ({animatedObjectPosition.X:F3},{animatedObjectPosition.Y:F3},{animatedObjectPosition.Z:F3})");
|
||||
|
||||
if (collisionObjectPosition != null)
|
||||
{
|
||||
LogManager.Debug($"[碰撞缓存-位置] 碰撞对象位置: ({collisionObjectPosition.X:F3},{collisionObjectPosition.Y:F3},{collisionObjectPosition.Z:F3})");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug($"[碰撞缓存-位置] 碰撞对象位置: NULL (将自动计算)");
|
||||
}
|
||||
|
||||
if (!IsModelItemValid(animatedObject) || !IsModelItemValid(collisionObject))
|
||||
{
|
||||
LogManager.Warning($"[诊断-无效对象] 对象验证失败,退出缓存过程");
|
||||
@ -229,43 +167,19 @@ namespace NavisworksTransport
|
||||
bool hasMapping1 = !mappedAnimatedObject.Equals(animatedObject);
|
||||
bool hasMapping2 = !mappedCollisionObject.Equals(collisionObject);
|
||||
|
||||
LogManager.Info($"[容器映射] 动画对象: '{animatedObject.DisplayName}' -> '{mappedAnimatedObject.DisplayName}' (映射: {hasMapping1})");
|
||||
LogManager.Info($"[容器映射] 碰撞对象: '{collisionObject.DisplayName}' -> '{mappedCollisionObject.DisplayName}' (映射: {hasMapping2})");
|
||||
|
||||
// 使用包围盒碰撞检测算法
|
||||
var animatedBoundingBox = animatedObject.BoundingBox();
|
||||
var collisionBoundingBox = collisionObject.BoundingBox();
|
||||
|
||||
LogManager.Debug($"[碰撞缓存-包围盒] 动画对象包围盒: Min({animatedBoundingBox.Min.X:F3},{animatedBoundingBox.Min.Y:F3},{animatedBoundingBox.Min.Z:F3}) Max({animatedBoundingBox.Max.X:F3},{animatedBoundingBox.Max.Y:F3},{animatedBoundingBox.Max.Z:F3})");
|
||||
LogManager.Debug($"[碰撞缓存-包围盒] 碰撞对象包围盒: Min({collisionBoundingBox.Min.X:F3},{collisionBoundingBox.Min.Y:F3},{collisionBoundingBox.Min.Z:F3}) Max({collisionBoundingBox.Max.X:F3},{collisionBoundingBox.Max.Y:F3},{collisionBoundingBox.Max.Z:F3})");
|
||||
|
||||
|
||||
if (BoundingBoxGeometryUtils.BoundingBoxesIntersect(animatedBoundingBox, collisionBoundingBox))
|
||||
{
|
||||
// 🔍 计算并验证位置信息
|
||||
var finalCollisionPosition = collisionObjectPosition ?? GetObjectPosition(collisionObject);
|
||||
|
||||
LogManager.Debug($"[碰撞缓存-位置计算] 最终使用的碰撞对象位置: ({finalCollisionPosition.X:F3},{finalCollisionPosition.Y:F3},{finalCollisionPosition.Z:F3})");
|
||||
|
||||
// 🔍 检查位置是否相同(可能的自碰撞标志)
|
||||
var positionDistance = Math.Sqrt(
|
||||
Math.Pow(animatedObjectPosition.X - finalCollisionPosition.X, 2) +
|
||||
Math.Pow(animatedObjectPosition.Y - finalCollisionPosition.Y, 2) +
|
||||
Math.Pow(animatedObjectPosition.Z - finalCollisionPosition.Z, 2));
|
||||
|
||||
LogManager.Debug($"[碰撞缓存-距离计算] 动画对象与碰撞对象的位置距离: {positionDistance:F4}");
|
||||
|
||||
if (positionDistance < 0.001) // 位置基本相同
|
||||
{
|
||||
LogManager.Warning($"[诊断-位置警告] 两个对象位置几乎相同 (距离: {positionDistance:F6}),这可能是自碰撞的标志!");
|
||||
}
|
||||
|
||||
|
||||
// 创建碰撞结果
|
||||
var collisionDistance = BoundingBoxGeometryUtils.CalculateDistance(animatedBoundingBox, collisionBoundingBox);
|
||||
var collisionCenter = BoundingBoxGeometryUtils.CalculateCenter(animatedBoundingBox, collisionBoundingBox);
|
||||
|
||||
LogManager.Debug($"[碰撞缓存-距离] 包围盒碰撞距离: {collisionDistance:F4}");
|
||||
LogManager.Debug($"[碰撞缓存-中心点] 碰撞中心: ({collisionCenter.X:F3},{collisionCenter.Y:F3},{collisionCenter.Z:F3})");
|
||||
|
||||
|
||||
var collision = new CollisionResult
|
||||
{
|
||||
ClashGuid = Guid.NewGuid(),
|
||||
@ -293,10 +207,6 @@ namespace NavisworksTransport
|
||||
_cachedResults.Add(collision);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug($"[碰撞缓存-包围盒] 包围盒不相交,跳过缓存");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -341,7 +251,7 @@ namespace NavisworksTransport
|
||||
{
|
||||
LogManager.Info($"=== 动画结束,开始创建ClashDetective碰撞测试(容差: {detectionGap}米) ===");
|
||||
|
||||
// 🔧 修复:在处理前记录动画过程中的碰撞数量
|
||||
// 在处理前记录动画过程中的碰撞数量
|
||||
_animationCollisionCount = _cachedResults.Count;
|
||||
LogManager.Info($"记录动画过程碰撞数量: {_animationCollisionCount}");
|
||||
|
||||
@ -352,34 +262,6 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
LogManager.Info($"[缓存分析-统计] 共有 {_cachedResults.Count} 个原始缓存结果");
|
||||
|
||||
// 🔍 详细分析每个缓存项
|
||||
LogManager.Info($"[缓存分析-原始] 开始分析所有原始缓存结果:");
|
||||
for (int i = 0; i < _cachedResults.Count; i++)
|
||||
{
|
||||
var item = _cachedResults[i];
|
||||
LogManager.Info($"[缓存分析-{i+1:00}] 对象: {item.Item1?.DisplayName ?? "NULL"} <-> {item.Item2?.DisplayName ?? "NULL"}");
|
||||
LogManager.Info($"[缓存分析-{i+1:00}] 时间: {item.CreatedTime:HH:mm:ss.fff}");
|
||||
LogManager.Info($"[缓存分析-{i+1:00}] 动画位置: ({item.Item1Position.X:F3},{item.Item1Position.Y:F3},{item.Item1Position.Z:F3})");
|
||||
LogManager.Info($"[缓存分析-{i+1:00}] 碰撞位置: ({item.Item2Position.X:F3},{item.Item2Position.Y:F3},{item.Item2Position.Z:F3})");
|
||||
LogManager.Info($"[缓存分析-{i+1:00}] 距离: {item.Distance:F4}");
|
||||
LogManager.Info($"[缓存分析-{i+1:00}] GUID: {item.ClashGuid}");
|
||||
LogManager.Info($"[缓存分析-{i+1:00}] HasPositionInfo: {item.HasPositionInfo}");
|
||||
|
||||
// 验证对象有效性
|
||||
var item1Valid = IsModelItemValid(item.Item1);
|
||||
var item2Valid = IsModelItemValid(item.Item2);
|
||||
LogManager.Info($"[缓存分析-{i+1:00}] 对象有效性: Item1={item1Valid}, Item2={item2Valid}");
|
||||
|
||||
if (!item1Valid)
|
||||
{
|
||||
LogManager.Warning($"[缓存分析-{i+1:00}] Item1无效: {item.Item1?.DisplayName ?? "NULL"}");
|
||||
}
|
||||
if (!item2Valid)
|
||||
{
|
||||
LogManager.Warning($"[缓存分析-{i+1:00}] Item2无效: {item.Item2?.DisplayName ?? "NULL"}");
|
||||
}
|
||||
}
|
||||
|
||||
// 获取动画对象和当前动画终点位置(用于后续恢复)
|
||||
ModelItem animatedObject = null;
|
||||
@ -419,38 +301,8 @@ namespace NavisworksTransport
|
||||
|
||||
LogManager.Info($"[去重分析-结果] 去重后得到 {uniqueCollisions.Count} 个唯一碰撞对");
|
||||
LogManager.Info($"[去重分析-效率] 去重前: {_cachedResults.Count}, 去重后: {uniqueCollisions.Count}, 压缩率: {(1.0 - (double)uniqueCollisions.Count / _cachedResults.Count) * 100:F1}%");
|
||||
|
||||
// 🔍 分析每个唯一碰撞对的详情
|
||||
for (int i = 0; i < uniqueCollisions.Count; i++)
|
||||
{
|
||||
var group = uniqueCollisions[i];
|
||||
LogManager.Info($"[去重分析-唯一{i+1:00}] 碰撞对: {group.Collision.Item1?.DisplayName ?? "NULL"} <-> {group.Collision.Item2?.DisplayName ?? "NULL"}");
|
||||
LogManager.Info($"[去重分析-唯一{i+1:00}] 重复次数: {group.Count}");
|
||||
LogManager.Info($"[去重分析-唯一{i+1:00}] 时间范围: {group.FirstTime:HH:mm:ss.fff} ~ {group.LastTime:HH:mm:ss.fff}");
|
||||
LogManager.Info($"[去重分析-唯一{i+1:00}] 选择记录: 时间={group.Collision.CreatedTime:HH:mm:ss.fff}, 距离={group.Collision.Distance:F4}");
|
||||
|
||||
// 如果有重复,分析所有重复项
|
||||
if (group.Count > 1)
|
||||
{
|
||||
LogManager.Info($"[去重分析-唯一{i+1:00}] 重复项详情:");
|
||||
for (int j = 0; j < group.AllItems.Count; j++)
|
||||
{
|
||||
var dupItem = group.AllItems[j];
|
||||
LogManager.Info($" [{j+1}] 时间: {dupItem.CreatedTime:HH:mm:ss.fff}, 距离: {dupItem.Distance:F4}, GUID: {dupItem.ClashGuid}");
|
||||
}
|
||||
}
|
||||
|
||||
// 验证去重键的具体值
|
||||
var key1Name = group.Collision.Item1?.DisplayName ?? "NULL";
|
||||
var key2Name = group.Collision.Item2?.DisplayName ?? "NULL";
|
||||
var key1Guid = group.Collision.Item1?.InstanceGuid.ToString() ?? "NULL";
|
||||
var key2Guid = group.Collision.Item2?.InstanceGuid.ToString() ?? "NULL";
|
||||
|
||||
LogManager.Debug($"[去重分析-唯一{i+1:00}] 去重键: Item1Name='{key1Name}', Item2Name='{key2Name}'");
|
||||
LogManager.Debug($"[去重分析-唯一{i+1:00}] 去重键: Item1GUID='{key1Guid}', Item2GUID='{key2Guid}'");
|
||||
}
|
||||
|
||||
var doc = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||
var doc = Application.ActiveDocument;
|
||||
|
||||
// 使用分组方案:创建一个主测试和一个包含所有碰撞结果的组
|
||||
LogManager.Info("=== 开始分组方案:创建主测试和碰撞分组 ===");
|
||||
@ -882,8 +734,6 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime _lastTestCreationTime = DateTime.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// 高亮显示碰撞对象
|
||||
/// </summary>
|
||||
@ -926,12 +776,6 @@ namespace NavisworksTransport
|
||||
|
||||
// 高亮碰撞对象(使用指定颜色)
|
||||
doc.Models.OverrideTemporaryColor(collidingItems, highlightColor);
|
||||
|
||||
LogManager.Info($"高亮显示 {collidingItems.Count} 个碰撞对象,颜色: {highlightColor}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug("没有碰撞结果需要高亮");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -1017,12 +861,6 @@ namespace NavisworksTransport
|
||||
|
||||
// 记录活跃高亮
|
||||
_activeHighlights[category] = collidingItems;
|
||||
|
||||
LogManager.Info($"类别 '{category}' 高亮显示 {collidingItems.Count} 个碰撞对象,颜色: {highlightColor}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug($"类别 '{category}' 没有碰撞结果需要高亮");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1096,17 +934,14 @@ namespace NavisworksTransport
|
||||
/// <summary>
|
||||
/// 基于包围盒的快速碰撞检测(不用Clash Detective)
|
||||
/// </summary>
|
||||
private List<CollisionResult> DetectCollisionsSimple(ModelItem animatedObject,
|
||||
public List<CollisionResult> DetectCollisions(ModelItem animatedObject,
|
||||
ModelItemCollection excludeObjects, double detectionGap)
|
||||
{
|
||||
var results = new List<CollisionResult>();
|
||||
|
||||
try
|
||||
{
|
||||
LogManager.Debug($"开始快速碰撞检测,动画对象: {animatedObject.DisplayName}");
|
||||
|
||||
{
|
||||
var animatedBoundingBox = animatedObject.BoundingBox();
|
||||
LogManager.Debug($"动画对象包围盒: Min({animatedBoundingBox.Min.X:F2}, {animatedBoundingBox.Min.Y:F2}, {animatedBoundingBox.Min.Z:F2}) Max({animatedBoundingBox.Max.X:F2}, {animatedBoundingBox.Max.Y:F2}, {animatedBoundingBox.Max.Z:F2})");
|
||||
|
||||
// 性能分析计时器
|
||||
var exclusionStopwatch = new System.Diagnostics.Stopwatch();
|
||||
@ -1135,7 +970,6 @@ namespace NavisworksTransport
|
||||
}
|
||||
}
|
||||
exclusionStopwatch.Stop();
|
||||
LogManager.Debug($"构建排除列表完成,耗时: {exclusionStopwatch.ElapsedMilliseconds}ms,排除对象: {exclusionList.Count} 个");
|
||||
|
||||
// 🔥 使用预构建的缓存获取对象列表
|
||||
getAllItemsStopwatch.Start();
|
||||
@ -1147,7 +981,6 @@ namespace NavisworksTransport
|
||||
{
|
||||
// 从缓存中过滤掉动画对象本身
|
||||
itemList = _allGeometryItemsCache.Where(item => !item.Equals(animatedObject)).ToList();
|
||||
LogManager.Debug($"使用缓存获取对象列表,缓存对象总数: {_allGeometryItemsCache.Count},过滤后: {itemList.Count}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1160,7 +993,6 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
getAllItemsStopwatch.Stop();
|
||||
LogManager.Debug($"获取对象列表完成,耗时: {getAllItemsStopwatch.ElapsedMilliseconds}ms,对象总数: {itemList.Count}");
|
||||
|
||||
int checkedCount = 0;
|
||||
int excludedCount = 0;
|
||||
@ -1217,10 +1049,7 @@ namespace NavisworksTransport
|
||||
// 检查是否进行了容器映射
|
||||
bool hasMapping1 = !mappedAnimatedObject.Equals(animatedObject);
|
||||
bool hasMapping2 = !mappedCollisionObject.Equals(item);
|
||||
|
||||
LogManager.Info($"[容器映射] 原始对象: {animatedObject.DisplayName} -> 容器对象: {mappedAnimatedObject.DisplayName} (映射: {hasMapping1})");
|
||||
LogManager.Info($"[容器映射] 原始对象: {item.DisplayName} -> 容器对象: {mappedCollisionObject.DisplayName} (映射: {hasMapping2})");
|
||||
|
||||
|
||||
var result = new CollisionResult
|
||||
{
|
||||
ClashGuid = Guid.NewGuid(),
|
||||
@ -1237,7 +1066,6 @@ namespace NavisworksTransport
|
||||
};
|
||||
|
||||
results.Add(result);
|
||||
LogManager.Info($"检测到碰撞: {mappedAnimatedObject.DisplayName} <-> {mappedCollisionObject.DisplayName},距离: {result.Distance:F2}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1340,11 +1168,9 @@ namespace NavisworksTransport
|
||||
try
|
||||
{
|
||||
var document = Application.ActiveDocument;
|
||||
LogManager.Debug("[通道缓存] 开始使用SearchAPI构建通道对象缓存");
|
||||
|
||||
// 🔥 一行调用:直接获取所有可通行的物流模型项(使用优化后的SearchAPI)
|
||||
// 获取所有可通行的物流模型项
|
||||
var allChannelItems = CategoryAttributeManager.GetAllTraversableLogisticsItems(document);
|
||||
LogManager.Info($"[通道缓存] 使用SearchAPI直接获取到 {allChannelItems.Count} 个可通行物流元素");
|
||||
|
||||
if (allChannelItems.Count == 0)
|
||||
{
|
||||
@ -1357,19 +1183,14 @@ namespace NavisworksTransport
|
||||
foreach (var channelItem in allChannelItems)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.Debug($"[通道收集] 开始处理通道节点: '{channelItem.DisplayName}'");
|
||||
|
||||
// 🔥 直接使用 ModelItemAnalysisHelper 的方法收集相关节点
|
||||
{
|
||||
var itemRelatedNodes = ModelItemAnalysisHelper.CollectRelatedNodes(channelItem);
|
||||
|
||||
// 将结果添加到缓存中
|
||||
foreach (var node in itemRelatedNodes)
|
||||
{
|
||||
_channelObjectsCache.Add(node);
|
||||
}
|
||||
|
||||
LogManager.Debug($"[通道收集] 从 '{channelItem.DisplayName}' 收集到 {itemRelatedNodes.Count} 个相关节点");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1383,21 +1204,6 @@ namespace NavisworksTransport
|
||||
LogManager.Info($"通道对象缓存构建完成,耗时: {cacheStopwatch.ElapsedMilliseconds}ms");
|
||||
LogManager.Info($" - 可通行物流根对象: {allChannelItems.Count} 个");
|
||||
LogManager.Info($" - 缓存总对象数: {_channelObjectsCache.Count} 个");
|
||||
|
||||
// 列出找到的通道根对象
|
||||
if (allChannelItems.Count > 0)
|
||||
{
|
||||
LogManager.Info("[通道缓存] 找到的可通行物流对象列表:");
|
||||
for (int i = 0; i < Math.Min(allChannelItems.Count, 10); i++) // 最多显示前10个
|
||||
{
|
||||
var item = allChannelItems[i];
|
||||
LogManager.Info($" {i + 1}. {item.DisplayName} (HasGeometry: {item.HasGeometry})");
|
||||
}
|
||||
if (allChannelItems.Count > 10)
|
||||
{
|
||||
LogManager.Info($" ... 还有 {allChannelItems.Count - 10} 个对象(省略显示)");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1520,9 +1326,7 @@ namespace NavisworksTransport
|
||||
|
||||
// 否则查找有名称的父级容器
|
||||
var containerObject = ModelItemAnalysisHelper.FindNamedParentContainer(originalItem);
|
||||
|
||||
LogManager.Info($"[碰撞对象映射] 原对象: '{originalItem.DisplayName}' -> 容器对象: '{containerObject?.DisplayName}'");
|
||||
|
||||
|
||||
return containerObject ?? originalItem;
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ namespace NavisworksTransport
|
||||
|
||||
private CoordinateConverter _coordinateConverter;
|
||||
private PathPointRenderPlugin _renderPlugin;
|
||||
private List<ModelItem> _selectedChannels;
|
||||
private List<ModelItem> _walkableAreas;
|
||||
private List<PathRoute> _routes;
|
||||
private PathRoute _currentRoute;
|
||||
private ChannelBounds _combinedChannelBounds;
|
||||
@ -131,7 +131,7 @@ namespace NavisworksTransport
|
||||
_renderPlugin = null;
|
||||
}
|
||||
|
||||
_selectedChannels = new List<ModelItem>();
|
||||
_walkableAreas = new List<ModelItem>();
|
||||
_routes = new List<PathRoute>();
|
||||
_currentRoute = new PathRoute("默认路径");
|
||||
_historyManager = new PathHistoryManager(50); // 最多保存50个历史记录
|
||||
@ -195,7 +195,7 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
internal List<PathRoute> ModifiableRoutes => _routes;
|
||||
|
||||
public IReadOnlyList<ModelItem> SelectedChannels => _selectedChannels.AsReadOnly();
|
||||
public IReadOnlyList<ModelItem> SelectedChannels => _walkableAreas.AsReadOnly();
|
||||
|
||||
public PathPointType CurrentPointType
|
||||
{
|
||||
@ -1082,7 +1082,7 @@ namespace NavisworksTransport
|
||||
{
|
||||
try
|
||||
{
|
||||
_selectedChannels.Clear();
|
||||
_walkableAreas.Clear();
|
||||
|
||||
if (useCurrentSelection)
|
||||
{
|
||||
@ -1090,8 +1090,8 @@ namespace NavisworksTransport
|
||||
var currentSelection = Application.ActiveDocument.CurrentSelection.SelectedItems;
|
||||
if (currentSelection.Any())
|
||||
{
|
||||
_selectedChannels.AddRange(currentSelection);
|
||||
RaiseStatusChanged($"已选择 {_selectedChannels.Count} 个模型作为通道", PathPlanningStatusType.Success);
|
||||
_walkableAreas.AddRange(currentSelection);
|
||||
RaiseStatusChanged($"已选择 {_walkableAreas.Count} 个模型作为通道", PathPlanningStatusType.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1107,23 +1107,23 @@ namespace NavisworksTransport
|
||||
{
|
||||
var allLogisticsItems = CategoryAttributeManager.GetAllLogisticsItems(document);
|
||||
var channelItems = CategoryAttributeManager.FilterByLogisticsType(allLogisticsItems, CategoryAttributeManager.LogisticsElementType.通道);
|
||||
_selectedChannels.AddRange(channelItems);
|
||||
_walkableAreas.AddRange(channelItems);
|
||||
LogManager.Info($"[SelectChannels] 通过CategoryAttributeManager直接筛选到 {channelItems.Count} 个通道");
|
||||
}
|
||||
|
||||
RaiseStatusChanged($"通过类别筛选到 {_selectedChannels.Count} 个通道", PathPlanningStatusType.Success);
|
||||
RaiseStatusChanged($"通过类别筛选到 {_walkableAreas.Count} 个通道", PathPlanningStatusType.Success);
|
||||
}
|
||||
|
||||
if (_selectedChannels.Any())
|
||||
if (_walkableAreas.Any())
|
||||
{
|
||||
// 计算组合边界
|
||||
CalculateCombinedBounds();
|
||||
|
||||
// 触发通道选择变更事件
|
||||
RaiseChannelSelectionChanged(_selectedChannels, useCurrentSelection ? "手动选择" : "类别筛选");
|
||||
RaiseChannelSelectionChanged(_walkableAreas, useCurrentSelection ? "手动选择" : "类别筛选");
|
||||
}
|
||||
|
||||
return _selectedChannels.Count;
|
||||
return _walkableAreas.Count;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -1254,7 +1254,7 @@ namespace NavisworksTransport
|
||||
|
||||
// 更新路径关联的通道ID
|
||||
route.AssociatedChannelIds.Clear();
|
||||
foreach (var channel in _selectedChannels)
|
||||
foreach (var channel in _walkableAreas)
|
||||
{
|
||||
route.AssociatedChannelIds.Add(channel.InstanceGuid.ToString());
|
||||
}
|
||||
@ -1294,7 +1294,7 @@ namespace NavisworksTransport
|
||||
AutoSelectLogisticsChannels();
|
||||
|
||||
// 检查是否有可通行的物流模型
|
||||
if (_selectedChannels == null || _selectedChannels.Count == 0)
|
||||
if (_walkableAreas == null || _walkableAreas.Count == 0)
|
||||
{
|
||||
RaiseErrorOccurred("没有找到任何可通行的物流模型,请先为模型设置可通行的物流属性");
|
||||
// 重置状态
|
||||
@ -1974,7 +1974,7 @@ namespace NavisworksTransport
|
||||
/// <returns>统计信息字符串</returns>
|
||||
public string GetStatistics()
|
||||
{
|
||||
var stats = $"通道数量: {_selectedChannels.Count}\n";
|
||||
var stats = $"通道数量: {_walkableAreas.Count}\n";
|
||||
stats += $"路径数量: {_routes.Count}\n";
|
||||
|
||||
if (_currentRoute != null)
|
||||
@ -2021,7 +2021,7 @@ namespace NavisworksTransport
|
||||
errors.Add("当前文档中没有加载的模型");
|
||||
|
||||
// 检查通道选择状态
|
||||
if (_selectedChannels.Count == 0)
|
||||
if (_walkableAreas.Count == 0)
|
||||
errors.Add("没有选择任何通道模型");
|
||||
|
||||
if (errors.Count > 0)
|
||||
@ -2116,7 +2116,7 @@ namespace NavisworksTransport
|
||||
private void CalculateCombinedBounds()
|
||||
{
|
||||
// 保留原有实现,但移除UI相关调用
|
||||
if (!_selectedChannels.Any())
|
||||
if (!_walkableAreas.Any())
|
||||
{
|
||||
_combinedChannelBounds = null;
|
||||
return;
|
||||
@ -2129,7 +2129,7 @@ namespace NavisworksTransport
|
||||
|
||||
var allBounds = new List<BoundingBox3D>();
|
||||
|
||||
foreach (var channel in _selectedChannels)
|
||||
foreach (var channel in _walkableAreas)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -2214,7 +2214,7 @@ namespace NavisworksTransport
|
||||
var document = Application.ActiveDocument;
|
||||
if (document?.Models == null) return;
|
||||
|
||||
_selectedChannels.Clear();
|
||||
_walkableAreas.Clear();
|
||||
|
||||
LogManager.Info("[通道自动选择] 开始搜索可通行的物流模型");
|
||||
|
||||
@ -2229,19 +2229,19 @@ namespace NavisworksTransport
|
||||
// 将可通行的物流模型添加到_selectedChannels
|
||||
foreach (ModelItem item in traversableItems)
|
||||
{
|
||||
_selectedChannels.Add(item);
|
||||
_walkableAreas.Add(item);
|
||||
LogManager.Info($"[通道自动选择] 添加可通行模型: '{item.DisplayName}'");
|
||||
}
|
||||
|
||||
if (_selectedChannels.Count > 0)
|
||||
if (_walkableAreas.Count > 0)
|
||||
{
|
||||
// 计算组合边界
|
||||
CalculateCombinedBounds();
|
||||
|
||||
// 触发通道选择变更事件
|
||||
RaiseChannelSelectionChanged(_selectedChannels, "自动选择");
|
||||
RaiseChannelSelectionChanged(_walkableAreas, "自动选择");
|
||||
|
||||
LogManager.Info($"[通道自动选择] ✅ 自动选择了 {_selectedChannels.Count} 个可通行的物流模型");
|
||||
LogManager.Info($"[通道自动选择] ✅ 自动选择了 {_walkableAreas.Count} 个可通行的物流模型");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2373,8 +2373,8 @@ namespace NavisworksTransport
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[ToolPlugin] 激活异常: {ex.Message}");
|
||||
LogManager.WriteLog($"[ToolPlugin] 堆栈: {ex.StackTrace}");
|
||||
LogManager.Error($"[ToolPlugin] 激活异常: {ex.Message}");
|
||||
LogManager.Error($"[ToolPlugin] 堆栈: {ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -2425,7 +2425,7 @@ namespace NavisworksTransport
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[ReactivateToolPlugin] 重新激活失败: {ex.Message}");
|
||||
LogManager.Error($"[ReactivateToolPlugin] 重新激活失败: {ex.Message}");
|
||||
RaiseErrorOccurred($"重新激活工具失败: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
@ -2437,11 +2437,6 @@ namespace NavisworksTransport
|
||||
{
|
||||
try
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin事件-V2] ===== 收到精确点击坐标 =====");
|
||||
LogManager.WriteLog($"[ToolPlugin事件-V2] 精确坐标: ({pickResult.Point.X:F3}, {pickResult.Point.Y:F3}, {pickResult.Point.Z:F3})");
|
||||
LogManager.WriteLog($"[ToolPlugin事件-V2] 选中对象: {pickResult.ModelItem?.DisplayName ?? "NULL"}");
|
||||
LogManager.WriteLog($"[ToolPlugin事件-V2] 当前点类型: {CurrentPointType}");
|
||||
|
||||
// 如果在自动路径规划模式,则跳过处理(应该由PathEditingViewModel处理)
|
||||
if (IsInAutoPathMode)
|
||||
{
|
||||
@ -2454,8 +2449,8 @@ namespace NavisworksTransport
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[ToolPlugin事件] 处理异常: {ex.Message}");
|
||||
LogManager.WriteLog($"[ToolPlugin事件] 堆栈: {ex.StackTrace}");
|
||||
LogManager.Error($"[ToolPlugin事件] 处理异常: {ex.Message}");
|
||||
LogManager.Error($"[ToolPlugin事件] 堆栈: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -2465,17 +2460,17 @@ namespace NavisworksTransport
|
||||
private void ProcessManualPathEditing(PickItemResult pickResult)
|
||||
{
|
||||
// 检查当前选中的通道状态
|
||||
LogManager.WriteLog($"[手动编辑] 当前_selectedChannels状态: {(_selectedChannels == null ? "NULL" : $"包含{_selectedChannels.Count}个项目")}");
|
||||
LogManager.WriteLog($"[手动编辑] 当前_selectedChannels状态: {(_walkableAreas == null ? "NULL" : $"包含{_walkableAreas.Count}个项目")}");
|
||||
|
||||
// 如果没有选中的通道,尝试实时搜索
|
||||
if (_selectedChannels == null || _selectedChannels.Count == 0)
|
||||
if (_walkableAreas == null || _walkableAreas.Count == 0)
|
||||
{
|
||||
LogManager.WriteLog("[手动编辑] 没有预选通道,开始实时搜索可通行的物流模型");
|
||||
SearchAndSetTraversableChannels();
|
||||
}
|
||||
|
||||
// 检查是否在可通行的物流模型内并处理点击
|
||||
if (_selectedChannels != null && _selectedChannels.Any())
|
||||
if (_walkableAreas != null && _walkableAreas.Any())
|
||||
{
|
||||
bool isInTraversableLogisticsModel = IsItemInSelectedChannels(pickResult.ModelItem) ||
|
||||
IsItemChildOfSelectedChannels(pickResult.ModelItem);
|
||||
@ -2557,12 +2552,12 @@ namespace NavisworksTransport
|
||||
LogManager.WriteLog($"[搜索通道] 筛选出 {traversableItems.Count} 个可通行的物流模型");
|
||||
|
||||
// 临时设置为选中通道
|
||||
if (_selectedChannels == null) _selectedChannels = new List<ModelItem>();
|
||||
_selectedChannels.Clear();
|
||||
if (_walkableAreas == null) _walkableAreas = new List<ModelItem>();
|
||||
_walkableAreas.Clear();
|
||||
|
||||
foreach (ModelItem item in traversableItems)
|
||||
{
|
||||
_selectedChannels.Add(item);
|
||||
_walkableAreas.Add(item);
|
||||
LogManager.WriteLog($"[搜索通道] 添加可通行模型: '{item.DisplayName}'");
|
||||
}
|
||||
}
|
||||
@ -2647,19 +2642,14 @@ namespace NavisworksTransport
|
||||
try
|
||||
{
|
||||
// 清理资源
|
||||
if (_renderPlugin != null)
|
||||
{
|
||||
_renderPlugin.ClearAllPaths();
|
||||
_renderPlugin = null;
|
||||
}
|
||||
_renderPlugin?.ClearAllPaths();
|
||||
_renderPlugin = null;
|
||||
|
||||
// 停用ToolPlugin
|
||||
if (_isToolPluginActive)
|
||||
{
|
||||
DeactivateToolPlugin();
|
||||
}
|
||||
|
||||
LogManager.Info($"PathPlanningManager已释放,ManagerId: {_managerId}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -2677,20 +2667,14 @@ namespace NavisworksTransport
|
||||
|
||||
try
|
||||
{
|
||||
LogManager.WriteLog("[ToolPlugin] ===== 开始停用ToolPlugin =====");
|
||||
|
||||
// 1. 取消事件订阅
|
||||
LogManager.WriteLog("[ToolPlugin] 步骤1: 取消事件订阅");
|
||||
PathClickToolPlugin.MouseClicked -= OnToolPluginMouseClicked;
|
||||
LogManager.WriteLog("[ToolPlugin] ✓ 事件订阅已取消");
|
||||
|
||||
// 2. 重置为无活动工具状态
|
||||
LogManager.WriteLog("[ToolPlugin] 步骤2: 重置为默认工具");
|
||||
try
|
||||
{
|
||||
// 使用Tool.None重置到无活动工具状态,让Navisworks处理默认导航
|
||||
Application.MainDocument.Tool.Value = Tool.None;
|
||||
LogManager.WriteLog("[ToolPlugin] ✓ 已重置工具为None状态");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -2703,8 +2687,8 @@ namespace NavisworksTransport
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.WriteLog($"[ToolPlugin] 停用异常: {ex.Message}");
|
||||
LogManager.WriteLog($"[ToolPlugin] 堆栈: {ex.StackTrace}");
|
||||
LogManager.Error($"[ToolPlugin] 停用异常: {ex.Message}");
|
||||
LogManager.Error($"[ToolPlugin] 堆栈: {ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -2762,59 +2746,27 @@ namespace NavisworksTransport
|
||||
{
|
||||
try
|
||||
{
|
||||
var document = Application.ActiveDocument;
|
||||
if (document?.Models == null || !document.Models.Any())
|
||||
// 如果没有选中的可通行区域,自动搜索
|
||||
if (_walkableAreas == null || _walkableAreas.Count == 0)
|
||||
{
|
||||
LogManager.Warning("文档中没有模型");
|
||||
return null;
|
||||
LogManager.Info("未找到预定义的可通行区域,尝试自动搜索...");
|
||||
AutoSelectLogisticsChannels();
|
||||
}
|
||||
|
||||
// 如果有选中的通道,使用通道边界
|
||||
if (_selectedChannels != null && _selectedChannels.Count > 0)
|
||||
// 检查是否找到可通行区域
|
||||
if (_walkableAreas == null || _walkableAreas.Count == 0)
|
||||
{
|
||||
LogManager.Info($"使用选中通道边界,通道数量: {_selectedChannels.Count}");
|
||||
return CalculateChannelsBounds(_selectedChannels);
|
||||
LogManager.Error("没有找到任何可通行区域(通道、门、电梯、楼梯等)");
|
||||
throw new InvalidOperationException("无法进行路径规划:模型中没有定义可通行的物流区域。请先设置物流属性。");
|
||||
}
|
||||
|
||||
// 否则使用限制范围的模型边界
|
||||
LogManager.Info("使用限制范围的模型边界");
|
||||
var allBounds = new List<BoundingBox3D>();
|
||||
|
||||
foreach (var model in document.Models)
|
||||
{
|
||||
if (model.RootItem != null)
|
||||
{
|
||||
var modelBounds = model.RootItem.BoundingBox();
|
||||
if (modelBounds != null)
|
||||
{
|
||||
allBounds.Add(modelBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!allBounds.Any())
|
||||
{
|
||||
LogManager.Warning("无法获取任何模型的边界框");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算组合边界
|
||||
var minX = allBounds.Min(b => b.Min.X);
|
||||
var minY = allBounds.Min(b => b.Min.Y);
|
||||
var minZ = allBounds.Min(b => b.Min.Z);
|
||||
var maxX = allBounds.Max(b => b.Max.X);
|
||||
var maxY = allBounds.Max(b => b.Max.Y);
|
||||
var maxZ = allBounds.Max(b => b.Max.Z);
|
||||
|
||||
return new BoundingBox3D(
|
||||
new Point3D(minX, minY, minZ),
|
||||
new Point3D(maxX, maxY, maxZ)
|
||||
);
|
||||
// 使用可通行区域的边界作为路径规划范围
|
||||
return CalculateChannelsBounds(_walkableAreas);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"获取模型边界失败: {ex.Message}");
|
||||
return null;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2889,10 +2841,10 @@ namespace NavisworksTransport
|
||||
}
|
||||
|
||||
// 如果用户已选择通道,优先使用用户选择的通道
|
||||
if (_selectedChannels != null && _selectedChannels.Count > 0)
|
||||
if (_walkableAreas != null && _walkableAreas.Count > 0)
|
||||
{
|
||||
LogManager.Info($"[通道数据] 使用用户选择的通道,数量: {_selectedChannels.Count}");
|
||||
channelItems.AddRange(_selectedChannels);
|
||||
LogManager.Info($"[通道数据] 使用用户选择的通道,数量: {_walkableAreas.Count}");
|
||||
channelItems.AddRange(_walkableAreas);
|
||||
return channelItems;
|
||||
}
|
||||
|
||||
@ -3018,7 +2970,7 @@ namespace NavisworksTransport
|
||||
/// <returns>是否在选定通道中</returns>
|
||||
private bool IsItemInSelectedChannels(ModelItem item)
|
||||
{
|
||||
return _selectedChannels.Contains(item) || IsItemChildOfSelectedChannels(item);
|
||||
return _walkableAreas.Contains(item) || IsItemChildOfSelectedChannels(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -3028,7 +2980,7 @@ namespace NavisworksTransport
|
||||
/// <returns>是否为子项</returns>
|
||||
private bool IsItemChildOfSelectedChannels(ModelItem item)
|
||||
{
|
||||
foreach (var channel in _selectedChannels)
|
||||
foreach (var channel in _walkableAreas)
|
||||
{
|
||||
if (IsChildOf(item, channel))
|
||||
{
|
||||
|
||||
@ -703,8 +703,6 @@ namespace NavisworksTransport.Core
|
||||
_flushInterval, // 最小间隔50ms
|
||||
10 // 高优先级,UI更新很重要
|
||||
);
|
||||
|
||||
LogManager.Debug($"已注册UI更新队列处理到IdleEventManager,最小间隔: {_flushInterval}ms");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@ -758,17 +758,17 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
// 可以直线连接,更新最远索引
|
||||
farthestIndex = testIndex;
|
||||
LogManager.Debug($"[斜线优化] ✓ 成功连接:点{currentIndex}→点{testIndex},距离={distance:F2}m");
|
||||
//LogManager.Debug($"[斜线优化] ✓ 成功连接:点{currentIndex}→点{testIndex},距离={distance:F2}m");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 新增:记录失败详情
|
||||
var startGrid = gridMap.WorldToGrid(startPoint);
|
||||
var endGrid = gridMap.WorldToGrid(endPoint);
|
||||
LogManager.Debug($"[斜线优化] ✗ 连接失败:" +
|
||||
$"点{currentIndex}[网格({startGrid.X},{startGrid.Y})]→点{testIndex}[网格({endGrid.X},{endGrid.Y})]," +
|
||||
$"世界坐标:({startPoint.X:F2},{startPoint.Y:F2})→({endPoint.X:F2},{endPoint.Y:F2})," +
|
||||
$"距离={distance:F2}m,跨越{Math.Abs(endGrid.X-startGrid.X)+Math.Abs(endGrid.Y-startGrid.Y)}个网格");
|
||||
// LogManager.Debug($"[斜线优化] ✗ 连接失败:" +
|
||||
// $"点{currentIndex}[网格({startGrid.X},{startGrid.Y})]→点{testIndex}[网格({endGrid.X},{endGrid.Y})]," +
|
||||
// $"世界坐标:({startPoint.X:F2},{startPoint.Y:F2})→({endPoint.X:F2},{endPoint.Y:F2})," +
|
||||
// $"距离={distance:F2}m,跨越{Math.Abs(endGrid.X-startGrid.X)+Math.Abs(endGrid.Y-startGrid.Y)}个网格");
|
||||
}
|
||||
// 🔧 关键改进:不像现有算法那样遇到失败就停止,而是继续尝试更远的点
|
||||
// 这样可以发现更多斜线连接机会
|
||||
@ -778,7 +778,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
if (farthestIndex > currentIndex + 1)
|
||||
{
|
||||
int skippedPoints = farthestIndex - currentIndex - 1;
|
||||
LogManager.Info($"[斜线优化] 从点{currentIndex}直连到点{farthestIndex},跳过{skippedPoints}个中间点");
|
||||
//LogManager.Info($"[斜线优化] 从点{currentIndex}直连到点{farthestIndex},跳过{skippedPoints}个中间点");
|
||||
}
|
||||
|
||||
currentIndex = farthestIndex;
|
||||
|
||||
@ -104,13 +104,11 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
if (gridMap != null)
|
||||
{
|
||||
LogManager.Info("[路径优化] 执行基于网格的路径简化");
|
||||
optimizedPoints = SimplifyGridBasedPath(optimizedPoints, gridMap);
|
||||
LogManager.Info($"[路径优化] 简化完成,点数:{originalCount} -> {optimizedPoints.Count}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info("[路径优化] 执行传统共线点简化");
|
||||
optimizedPoints = SimplifyCollinearPoints(optimizedPoints);
|
||||
LogManager.Info($"[路径优化] 简化完成,点数:{originalCount} -> {optimizedPoints.Count}");
|
||||
}
|
||||
@ -123,13 +121,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
// optimizedPoints = SmoothPath(optimizedPoints);
|
||||
}
|
||||
|
||||
// 3. 未来扩展:碰撞检测验证
|
||||
if (_config.EnableCollisionCheck)
|
||||
{
|
||||
LogManager.Info("[路径优化] 碰撞检测功能尚未实现");
|
||||
// ValidatePathCollision(optimizedPoints);
|
||||
}
|
||||
|
||||
// 创建优化后的路径
|
||||
var optimizedRoute = CreateOptimizedRoute(originalPath, optimizedPoints);
|
||||
|
||||
@ -160,26 +151,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
var worldPath = points.Select(p => p.Position).ToList();
|
||||
var gridPath = worldPath.Select(p => gridMap.WorldToGrid(p)).ToList();
|
||||
|
||||
// 检查重复点(调试用)
|
||||
int duplicateCount = 0;
|
||||
for (int i = 1; i < gridPath.Count; i++)
|
||||
{
|
||||
if (gridPath[i].Equals(gridPath[i-1]))
|
||||
{
|
||||
duplicateCount++;
|
||||
LogManager.Warning($"[路径优化] 发现重复网格点:索引 {i-1} 和 {i} 都是 ({gridPath[i].X}, {gridPath[i].Y})");
|
||||
}
|
||||
}
|
||||
|
||||
if (duplicateCount > 0)
|
||||
{
|
||||
LogManager.Warning($"[路径优化] 总共发现 {duplicateCount} 个重复网格点,开始去重处理");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Info("[路径优化] 未发现重复网格点,A*算法输出正常");
|
||||
}
|
||||
|
||||
// 步骤1:先去除重复点
|
||||
var dedupedGridPath = new List<GridPoint2D>();
|
||||
var dedupedPoints = new List<PathPoint>();
|
||||
@ -191,15 +162,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
dedupedPoints.Add(points[i]);
|
||||
}
|
||||
}
|
||||
|
||||
LogManager.Info($"[路径优化] 去重完成:{gridPath.Count} -> {dedupedGridPath.Count} 个点");
|
||||
|
||||
if (dedupedGridPath.Count < 3)
|
||||
{
|
||||
LogManager.Info("[路径优化] 去重后点数不足3个,直接返回");
|
||||
return dedupedPoints;
|
||||
}
|
||||
|
||||
|
||||
// 步骤2:基于去重后的网格路径进行方向优化
|
||||
var simplified = new List<GridPoint2D> { dedupedGridPath[0] };
|
||||
|
||||
@ -264,7 +227,6 @@ namespace NavisworksTransport.PathPlanning
|
||||
var simplified = new List<PathPoint>();
|
||||
simplified.Add(points[0]); // 添加起点
|
||||
|
||||
LogManager.Info($"[共线简化] 开始简化,原始点数:{points.Count}");
|
||||
int removedCount = 0;
|
||||
|
||||
// 遍历中间点,只保留转折点
|
||||
@ -281,13 +243,11 @@ namespace NavisworksTransport.PathPlanning
|
||||
{
|
||||
// 这是一个转折点,需要保留
|
||||
simplified.Add(currPoint);
|
||||
LogManager.Debug($"[共线简化] 保留转折点 {i}:{currPoint.Name}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 这是直线上的冗余点,跳过
|
||||
removedCount++;
|
||||
LogManager.Debug($"[共线简化] 移除冗余点 {i}:{currPoint.Name}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,8 +292,7 @@ namespace NavisworksTransport.PathPlanning
|
||||
|
||||
if (isSegment1Zero || isSegment2Zero)
|
||||
{
|
||||
// 🔥 关键修复:只有在真正重复点时才认为共线
|
||||
LogManager.Debug($"[共线检测] ✅ 包含真正重复点,视为共线:({p1.X:F3},{p1.Y:F3}) -> ({p2.X:F3},{p2.Y:F3}) -> ({p3.X:F3},{p3.Y:F3})");
|
||||
// 🔥 在真正重复点时才认为共线
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -348,39 +307,18 @@ namespace NavisworksTransport.PathPlanning
|
||||
if (isSegment1Horizontal && isSegment2Horizontal)
|
||||
{
|
||||
// 检查水平方向是否一致(同向或反向都可以)
|
||||
bool sameDirection = (dx12 * dx23 > 0) || Math.Abs(dx12) < tolerance || Math.Abs(dx23) < tolerance;
|
||||
if (sameDirection)
|
||||
{
|
||||
LogManager.Debug($"[共线检测] ✅ 水平共线:({p1.X:F3},{p1.Y:F3}) -> ({p2.X:F3},{p2.Y:F3}) -> ({p3.X:F3},{p3.Y:F3})");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug($"[共线检测] ❌ 水平线段方向相反:dx12={dx12:F3}, dx23={dx23:F3}");
|
||||
return false;
|
||||
}
|
||||
return (dx12 * dx23 > 0) || Math.Abs(dx12) < tolerance || Math.Abs(dx23) < tolerance;
|
||||
}
|
||||
else if (isSegment1Vertical && isSegment2Vertical)
|
||||
{
|
||||
// 检查垂直方向是否一致(同向或反向都可以)
|
||||
bool sameDirection = (dy12 * dy23 > 0) || Math.Abs(dy12) < tolerance || Math.Abs(dy23) < tolerance;
|
||||
if (sameDirection)
|
||||
{
|
||||
LogManager.Debug($"[共线检测] ✅ 垂直共线:({p1.X:F3},{p1.Y:F3}) -> ({p2.X:F3},{p2.Y:F3}) -> ({p3.X:F3},{p3.Y:F3})");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug($"[共线检测] ❌ 垂直线段方向相反:dy12={dy12:F3}, dy23={dy23:F3}");
|
||||
return false;
|
||||
}
|
||||
return (dy12 * dy23 > 0) || Math.Abs(dy12) < tolerance || Math.Abs(dy23) < tolerance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 🔥 关键:不同类型的线段(一个水平一个垂直,或包含斜线)一律拒绝
|
||||
string seg1Type = isSegment1Horizontal ? "水平" : (isSegment1Vertical ? "垂直" : "斜线");
|
||||
string seg2Type = isSegment2Horizontal ? "水平" : (isSegment2Vertical ? "垂直" : "斜线");
|
||||
LogManager.Debug($"[共线检测] ❌ 线段类型不匹配:线段1={seg1Type}({dx12:F3},{dy12:F3}), 线段2={seg2Type}({dx23:F3},{dy23:F3})");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,9 +82,7 @@ namespace NavisworksTransport.Utils
|
||||
{
|
||||
var nodeType = GetModelItemType(current);
|
||||
var displayName = GetSafeDisplayName(current);
|
||||
|
||||
LogManager.Debug($"[容器查找] 检查节点: '{displayName}' (类型:{nodeType}, 层级:{levels})");
|
||||
|
||||
|
||||
// 根据节点类型决策
|
||||
switch (nodeType)
|
||||
{
|
||||
@ -93,19 +91,11 @@ namespace NavisworksTransport.Utils
|
||||
// 集合节点或混合节点:有意义的容器,停止查找
|
||||
if (!string.IsNullOrEmpty(displayName))
|
||||
{
|
||||
LogManager.Debug($"[智能容器映射] 找到有意义容器: '{GetSafeDisplayName(geometryItem)}' -> '{displayName}' (向上{levels}层, 类型:{nodeType})");
|
||||
return current;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug($"[容器查找] {nodeType}节点无名称,继续向上查找");
|
||||
}
|
||||
break;
|
||||
|
||||
case ModelItemType.PureGeometry:
|
||||
case ModelItemType.EmptyNode:
|
||||
// 纯几何体或空节点:通常无意义,需要继续向上查找父节点
|
||||
LogManager.Debug($"[容器查找] {nodeType}节点,向上查找父节点");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -230,22 +220,18 @@ namespace NavisworksTransport.Utils
|
||||
|
||||
if (hasGeometry && childCount == 0)
|
||||
{
|
||||
LogManager.Debug($"[节点类型判断] '{item.DisplayName}' -> 纯几何体节点");
|
||||
return ModelItemType.PureGeometry;
|
||||
}
|
||||
else if (!hasGeometry && childCount > 0)
|
||||
{
|
||||
LogManager.Debug($"[节点类型判断] '{item.DisplayName}' -> 集合节点 (子节点数: {childCount})");
|
||||
return ModelItemType.GroupNode;
|
||||
}
|
||||
else if (hasGeometry && childCount > 0)
|
||||
{
|
||||
LogManager.Debug($"[节点类型判断] '{item.DisplayName}' -> 混合节点 (子节点数: {childCount})");
|
||||
return ModelItemType.HybridNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Debug($"[节点类型判断] '{item.DisplayName}' -> 空节点");
|
||||
return ModelItemType.EmptyNode;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user