402 lines
13 KiB
C#
402 lines
13 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Autodesk.Navisworks.Api;
|
||
|
||
namespace NavisworksTransport.Utils
|
||
{
|
||
/// <summary>
|
||
/// 统一管理 Navisworks 临时高亮的工具类,支持按类别高亮与清除。
|
||
/// </summary>
|
||
public static class ModelHighlightHelper
|
||
{
|
||
public const string PrecomputeCollisionResultsCategory = "collisionResults";
|
||
public const string ManualTargetsCategory = "manualTargets";
|
||
public const string ChannelPreviewCategory = "channelPreview";
|
||
public const string ClashDetectiveResultsCategory = "clashDetectiveResults";
|
||
public const string AnimatedObjectCategory = "animatedObject";
|
||
|
||
private class HighlightEntry
|
||
{
|
||
public Color Color { get; set; }
|
||
public ModelItemCollection Items { get; set; }
|
||
}
|
||
|
||
public class HighlightInfo
|
||
{
|
||
public string Category { get; set; } = string.Empty;
|
||
public Color Color { get; set; }
|
||
public int Count { get; set; }
|
||
}
|
||
|
||
private static readonly object _syncRoot = new object();
|
||
private static readonly Dictionary<string, HighlightEntry> _activeHighlights =
|
||
new Dictionary<string, HighlightEntry>(StringComparer.OrdinalIgnoreCase);
|
||
|
||
private static readonly Dictionary<string, Color> _categoryColors =
|
||
new Dictionary<string, Color>(StringComparer.OrdinalIgnoreCase)
|
||
{
|
||
{ "animation", Color.Red },
|
||
{ "runtime", Color.Red },
|
||
{ "independent", Color.Red },
|
||
{ "static", Color.Red },
|
||
{ "report", Color.Green },
|
||
{ "preview", Color.Blue },
|
||
{ ManualTargetsCategory, Color.FromByteRGB(255, 170, 0) },
|
||
{ PrecomputeCollisionResultsCategory, Color.FromByteRGB(244, 67, 54) }, // Material Red(预计算碰撞)
|
||
{ ChannelPreviewCategory, Color.Green },
|
||
{ ClashDetectiveResultsCategory, Color.Red },
|
||
{ AnimatedObjectCategory, Color.FromByteRGB(76, 175, 80) } // Material Green(动画车辆)
|
||
};
|
||
|
||
/// <summary>
|
||
/// 高亮指定对象集合(使用类别默认颜色)。
|
||
/// </summary>
|
||
public static void HighlightItems(string category, IEnumerable<ModelItem> items)
|
||
{
|
||
if (items == null)
|
||
return;
|
||
|
||
var document = Application.ActiveDocument;
|
||
if (document?.Models == null)
|
||
return;
|
||
|
||
if (string.IsNullOrWhiteSpace(category))
|
||
{
|
||
category = "default";
|
||
}
|
||
|
||
var color = GetCategoryColor(category);
|
||
|
||
lock (_syncRoot)
|
||
{
|
||
if (_activeHighlights.ContainsKey(category))
|
||
{
|
||
_activeHighlights.Remove(category);
|
||
ResetAllHighlights(document);
|
||
ReapplyHighlights(document);
|
||
}
|
||
|
||
var collection = BuildCollection(items);
|
||
if (collection.Count == 0)
|
||
{
|
||
_activeHighlights.Remove(category);
|
||
return;
|
||
}
|
||
|
||
document.Models.OverrideTemporaryColor(collection, color);
|
||
_activeHighlights[category] = new HighlightEntry
|
||
{
|
||
Color = color,
|
||
Items = collection
|
||
};
|
||
|
||
RequestRedraw(document);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除指定类别的高亮。
|
||
/// </summary>
|
||
public static void ClearCategory(string category)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(category))
|
||
return;
|
||
|
||
var document = Application.ActiveDocument;
|
||
if (document?.Models == null)
|
||
return;
|
||
|
||
lock (_syncRoot)
|
||
{
|
||
if (!_activeHighlights.Remove(category))
|
||
return;
|
||
|
||
ResetAllHighlights(document);
|
||
ReapplyHighlights(document);
|
||
RequestRedraw(document);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除所有碰撞相关高亮(预计算碰撞和ClashDetective碰撞)
|
||
/// </summary>
|
||
public static void ClearCollisionHighlights()
|
||
{
|
||
lock (_syncRoot)
|
||
{
|
||
ClearCategory(PrecomputeCollisionResultsCategory);
|
||
ClearCategory(ClashDetectiveResultsCategory);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除全部高亮。
|
||
/// </summary>
|
||
public static void ClearAllHighlights()
|
||
{
|
||
var document = Application.ActiveDocument;
|
||
if (document?.Models == null)
|
||
return;
|
||
|
||
lock (_syncRoot)
|
||
{
|
||
ResetAllHighlights(document);
|
||
_activeHighlights.Clear();
|
||
RequestRedraw(document);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前高亮快照。
|
||
/// </summary>
|
||
public static List<HighlightInfo> GetActiveHighlightSnapshot()
|
||
{
|
||
lock (_syncRoot)
|
||
{
|
||
return _activeHighlights
|
||
.Select(kvp => new HighlightInfo
|
||
{
|
||
Category = kvp.Key,
|
||
Color = kvp.Value.Color,
|
||
Count = kvp.Value.Items?.Count ?? 0
|
||
})
|
||
.ToList();
|
||
}
|
||
}
|
||
|
||
public static Color GetCategoryColor(string category)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(category))
|
||
{
|
||
category = "default";
|
||
}
|
||
|
||
if (_categoryColors.TryGetValue(category, out var color))
|
||
{
|
||
return color;
|
||
}
|
||
|
||
return Color.White;
|
||
}
|
||
|
||
private static ModelItemCollection BuildCollection(IEnumerable<ModelItem> items)
|
||
{
|
||
var collection = new ModelItemCollection();
|
||
if (items == null)
|
||
return collection;
|
||
|
||
foreach (var item in items)
|
||
{
|
||
if (item == null)
|
||
continue;
|
||
|
||
try
|
||
{
|
||
// 访问属性以检测对象是否有效
|
||
_ = item.DisplayName;
|
||
}
|
||
catch
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (!collection.Contains(item))
|
||
{
|
||
collection.Add(item);
|
||
}
|
||
}
|
||
|
||
return collection;
|
||
}
|
||
|
||
private static void ResetAllHighlights(Document document)
|
||
{
|
||
document.Models.ResetAllTemporaryMaterials();
|
||
}
|
||
|
||
private static void ReapplyHighlights(Document document)
|
||
{
|
||
foreach (var entry in _activeHighlights.Values)
|
||
{
|
||
if (entry.Items == null || entry.Items.Count == 0)
|
||
continue;
|
||
|
||
document.Models.OverrideTemporaryColor(entry.Items, entry.Color);
|
||
}
|
||
}
|
||
|
||
private static void RequestRedraw(Document document)
|
||
{
|
||
try
|
||
{
|
||
document.ActiveView?.RequestDelayedRedraw(ViewRedrawRequests.All);
|
||
}
|
||
catch
|
||
{
|
||
// 忽略重绘异常
|
||
}
|
||
}
|
||
|
||
#region 碰撞结果高亮相关方法
|
||
|
||
/// <summary>
|
||
/// 高亮显示碰撞对象(使用类别默认颜色)
|
||
/// </summary>
|
||
/// <param name="results">碰撞结果列表</param>
|
||
/// <param name="clearPrevious">是否清除之前的高亮</param>
|
||
public static void HighlightCollisionResults(List<CollisionResult> results, bool clearPrevious = true)
|
||
{
|
||
try
|
||
{
|
||
var validResults = results?.Where(r => r != null).ToList() ?? new List<CollisionResult>();
|
||
|
||
if (validResults.Count == 0)
|
||
{
|
||
if (clearPrevious)
|
||
{
|
||
ClearCategory(PrecomputeCollisionResultsCategory);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (clearPrevious)
|
||
{
|
||
ClearCategory(PrecomputeCollisionResultsCategory);
|
||
}
|
||
|
||
var highlightItems = new List<ModelItem>();
|
||
foreach (var collision in validResults)
|
||
{
|
||
var item1 = collision.Item1;
|
||
var item2 = collision.Item2;
|
||
|
||
// 只高亮被撞物体(Item2),排除车辆对象(Item1)
|
||
// 车辆对象由 AnimatedObjectCategory 单独高亮为绿色
|
||
if (item2 != null && !highlightItems.Contains(item2))
|
||
{
|
||
highlightItems.Add(item2);
|
||
}
|
||
}
|
||
|
||
HighlightItems(PrecomputeCollisionResultsCategory, highlightItems);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"高亮显示碰撞对象失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 按类别管理碰撞结果高亮
|
||
/// </summary>
|
||
/// <param name="category">类别名称</param>
|
||
/// <param name="results">碰撞结果列表</param>
|
||
/// <param name="clearOtherCategories">是否清除其他类别</param>
|
||
public static void ManageCollisionHighlightsByCategory(string category, List<CollisionResult> results,
|
||
bool clearOtherCategories = false)
|
||
{
|
||
try
|
||
{
|
||
var collidingItems = new List<ModelItem>();
|
||
|
||
if (results != null)
|
||
{
|
||
foreach (var result in results)
|
||
{
|
||
var item1 = result.Item1;
|
||
var item2 = result.Item2;
|
||
|
||
// 只高亮被撞物体(Item2),排除车辆对象(Item1)
|
||
if (item2 != null && !collidingItems.Contains(item2))
|
||
{
|
||
collidingItems.Add(item2);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (clearOtherCategories)
|
||
{
|
||
ClearCategory(category);
|
||
}
|
||
|
||
HighlightItems(category, collidingItems);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"按类别管理碰撞高亮失败 [{category}]: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 高亮ClashDetective测试结果
|
||
/// </summary>
|
||
/// <param name="testName">测试名称</param>
|
||
/// <param name="getResultsFunc">获取碰撞结果的函数</param>
|
||
public static void HighlightClashDetectiveResults(string testName, Func<string, List<CollisionResult>> getResultsFunc)
|
||
{
|
||
try
|
||
{
|
||
if (getResultsFunc == null)
|
||
{
|
||
LogManager.Warning("[ClashDetective高亮] 获取结果的函数为空");
|
||
return;
|
||
}
|
||
|
||
// 获取碰撞结果
|
||
var clashResults = getResultsFunc(testName);
|
||
|
||
if (clashResults == null || clashResults.Count == 0)
|
||
{
|
||
LogManager.Info("[ClashDetective高亮] 指定测试中未找到碰撞结果");
|
||
return;
|
||
}
|
||
|
||
// 提取碰撞对象
|
||
var highlightItems = new List<ModelItem>();
|
||
foreach (var clash in clashResults)
|
||
{
|
||
// 只高亮被撞物体(Item2),排除车辆对象(Item1)
|
||
// 🔥 移除去重逻辑:应该显示所有被撞物体,包括重复的
|
||
if (clash.Item2 != null)
|
||
{
|
||
highlightItems.Add(clash.Item2);
|
||
}
|
||
}
|
||
|
||
if (highlightItems.Count > 0)
|
||
{
|
||
HighlightItems(ClashDetectiveResultsCategory, highlightItems);
|
||
LogManager.Info($"[ClashDetective高亮] 已高亮 {highlightItems.Count} 个对象,来自 {clashResults.Count} 个碰撞结果");
|
||
}
|
||
else
|
||
{
|
||
LogManager.Info("[ClashDetective高亮] 没有有效的碰撞对象可高亮");
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ClashDetective高亮] 高亮失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除ClashDetective高亮
|
||
/// </summary>
|
||
public static void ClearClashDetectiveHighlights()
|
||
{
|
||
try
|
||
{
|
||
ClearCategory(ClashDetectiveResultsCategory);
|
||
LogManager.Info("[ClashDetective高亮] 已清除高亮");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogManager.Error($"[ClashDetective高亮] 清除高亮失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|