NavisworksTransport/PathPointRenderPlugin.cs
2025-06-21 17:38:48 +08:00

488 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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

using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Navisworks.Api;
using Autodesk.Navisworks.Api.Plugins;
namespace NavisworksTransport
{
/// <summary>
/// 路径点圆形标记渲染插件
/// 使用Graphics.Circle在3D空间绘制圆形标记
/// </summary>
[Plugin("PathPointRenderPlugin", "NavisworksTransport", DisplayName = "路径点圆形标记渲染器")]
public class PathPointRenderPlugin : RenderPlugin
{
private readonly object _lockObject = new object();
private List<CircleMarker> _circleMarkers = new List<CircleMarker>();
private bool _isEnabled = true;
// 静态实例,用于外部访问
private static PathPointRenderPlugin _instance;
/// <summary>
/// 构造函数
/// </summary>
public PathPointRenderPlugin()
{
_instance = this;
}
/// <summary>
/// 获取静态实例
/// </summary>
public static PathPointRenderPlugin Instance => _instance;
/// <summary>
/// 是否启用渲染
/// </summary>
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
// 触发视图刷新
if (Application.ActiveDocument?.ActiveView != null)
{
Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.Render);
}
}
}
/// <summary>
/// 当前圆形标记数量
/// </summary>
public int MarkerCount
{
get
{
lock (_lockObject)
{
return _circleMarkers.Count;
}
}
}
/// <summary>
/// Navisworks渲染回调方法
/// </summary>
/// <param name="view">当前视图</param>
/// <param name="graphics">图形上下文</param>
public override void Render(View view, Graphics graphics)
{
if (!_isEnabled) return;
try
{
// 首先检查文档和模型状态
var activeDoc = Application.ActiveDocument;
//LogManager.WriteLog($"[文档状态] ActiveDocument: {activeDoc?.GetType().Name ?? "null"}");
if (activeDoc == null)
{
LogManager.WriteLog($"[文档状态] ❌ 没有活动文档,跳过渲染");
return;
}
if (activeDoc.Models == null || activeDoc.Models.Count == 0)
{
LogManager.WriteLog($"[文档状态] ❌ 没有加载的模型,跳过渲染");
return;
}
//LogManager.WriteLog($"[文档状态] ✓ 文档正常,模型数量: {activeDoc.Models.Count}");
//LogManager.WriteLog($"[Graphics状态] Graphics对象: {graphics?.GetType().Name ?? "null"}");
//LogManager.WriteLog($"[Graphics状态] View对象: {view?.GetType().Name ?? "null"}");
// 使用BeginModelContext确保正确的渲染上下文
graphics.BeginModelContext();
//LogManager.WriteLog($"[Graphics状态] BeginModelContext完成");
lock (_lockObject)
{
// 绘制连接线段(作为圆柱体)
if (_circleMarkers.Count > 1)
{
graphics.Color(Color.FromByteRGB(0, 0, 0), 1.0); // 黑色连线
// 定义连线的物理半径例如20厘米
double lineRadiusInMeters = 0.2;
double lineRadiusInModelUnits = lineRadiusInMeters * GetMetersToModelUnitsConversionFactor();
for (int i = 0; i < _circleMarkers.Count - 1; i++)
{
var start = _circleMarkers[i].Center;
var end = _circleMarkers[i + 1].Center;
// 使用圆柱体来绘制具有物理尺寸的连线
graphics.Cylinder(start, end, lineRadiusInModelUnits);
}
}
// 遍历所有圆形标记并根据其自身属性绘制
foreach (var marker in _circleMarkers)
{
// 使用标记自身存储的颜色和不透明度
graphics.Color(marker.Color, marker.Alpha);
// 使用标记自身存储的半径
graphics.Sphere(marker.Center, marker.Radius);
}
}
// 结束ModelContext
graphics.EndModelContext();
//LogManager.WriteLog($"[Graphics状态] EndModelContext完成");
//LogManager.WriteLog($"[Graphics状态] === 3D渲染完成如果仍看不到图形问题可能在Graphics API兼容性 ===");
}
catch (Exception ex)
{
LogManager.WriteLog($"[圆形渲染] 渲染错误: {ex.Message}");
LogManager.WriteLog($"[圆形渲染] 异常堆栈: {ex.StackTrace}");
}
}
/// <summary>
/// 添加球体标记
/// </summary>
/// <param name="center">圆心位置</param>
/// <param name="pointType">路径点类型</param>
/// <param name="sequenceNumber">序号</param>
public void AddCircleMarker(Point3D center, PathPointType pointType, int sequenceNumber)
{
try
{
var marker = new CircleMarker
{
Center = center,
Normal = new Vector3D(0, 0, 1), // 垂直向上
Radius = GetRadiusForPointType(pointType),
Color = GetColorForPointType(pointType),
Alpha = 1.0, // 完全不透明
Filled = true, // 实心圆
PointType = pointType,
SequenceNumber = sequenceNumber,
CreatedTime = DateTime.Now
};
lock (_lockObject)
{
_circleMarkers.Add(marker);
}
//LogManager.WriteLog($"[圆形标记] 添加圆形标记: 类型={pointType}, 序号={sequenceNumber}, 中心=({center.X:F2}, {center.Y:F2}, {center.Z:F2})");
// 触发视图刷新
RequestViewRefresh();
}
catch (Exception ex)
{
LogManager.WriteLog($"[圆形标记] 添加标记失败: {ex.Message}");
}
}
/// <summary>
/// 移除指定位置的球体标记
/// </summary>
/// <param name="position">位置</param>
/// <param name="tolerance">容差距离(米)</param>
/// <returns>是否成功移除</returns>
public bool RemoveMarkerAt(Point3D position, double tolerance = 1.0)
{
bool removed = false;
try
{
lock (_lockObject)
{
for (int i = _circleMarkers.Count - 1; i >= 0; i--)
{
var marker = _circleMarkers[i];
var distance = CalculateDistance(marker.Center, position);
if (distance <= tolerance)
{
_circleMarkers.RemoveAt(i);
removed = true;
LogManager.WriteLog($"[球体标记] 移除标记: 序号={marker.SequenceNumber}, 距离={distance:F2}m");
}
}
}
if (removed)
{
RequestViewRefresh();
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[球体标记] 移除标记失败: {ex.Message}");
}
return removed;
}
/// <summary>
/// 清除所有球体标记
/// </summary>
public void ClearAllMarkers()
{
try
{
int removedCount = 0;
lock (_lockObject)
{
removedCount = _circleMarkers.Count;
_circleMarkers.Clear();
}
LogManager.WriteLog($"[球体标记] 清除所有标记,共移除 {removedCount} 个球体标记");
if (removedCount > 0)
{
RequestViewRefresh();
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[球体标记] 清除标记失败: {ex.Message}");
}
}
/// <summary>
/// 获取所有球体标记的副本
/// </summary>
/// <returns>标记列表</returns>
public List<CircleMarker> GetAllMarkers()
{
lock (_lockObject)
{
return new List<CircleMarker>(_circleMarkers);
}
}
/// <summary>
/// 根据序号更新一个已存在的球体标记
/// </summary>
public void UpdateMarker(int sequenceNumber, Color newColor, double newRadius)
{
try
{
lock (_lockObject)
{
var markerToUpdate = _circleMarkers.FirstOrDefault(m => m.SequenceNumber == sequenceNumber);
if (markerToUpdate != null)
{
markerToUpdate.Color = newColor;
markerToUpdate.Radius = newRadius;
LogManager.WriteLog($"[球体标记] 更新标记: 序号={sequenceNumber}, 新颜色={newColor}, 新半径={newRadius:F2}");
RequestViewRefresh();
}
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[球体标记] 更新标记失败: {ex.Message}");
}
}
#region
/// <summary>
/// 根据路径点类型和真实文档单位获取适当的半径
/// 目标路径点半径为0.5米物理尺寸,起点/终点为0.8米物理尺寸
/// </summary>
public double GetRadiusForPointType(PathPointType pointType)
{
// 基础半径米为单位起点和终点为0.5米路径点为0.4米
double baseRadiusInMeters = pointType == PathPointType.WayPoint ? 0.4 : 0.5;
// 获取真实文档单位转换系数
double metersToModelUnits = GetMetersToModelUnitsConversionFactor();
// 转换为模型单位
double radiusInModelUnits = baseRadiusInMeters * metersToModelUnits;
//LogManager.WriteLog($"[半径计算] 类型={pointType}, 基础半径={baseRadiusInMeters}m, 转换系数={metersToModelUnits:F2}, 最终半径={radiusInModelUnits:F2}");
return radiusInModelUnits;
}
/// <summary>
/// 获取米转换为文档单位的转换系数
/// 使用Navisworks API直接获取真实文档单位而不是猜测
/// </summary>
private double GetMetersToModelUnitsConversionFactor()
{
try
{
var units = Application.ActiveDocument.Units;
//LogManager.WriteLog($"[单位检测] API返回的文档单位: {units}");
switch (units)
{
case Units.Millimeters:
//LogManager.WriteLog("[单位检测] 确认为毫米单位,转换系数=1000");
return 1000.0; // 1米 = 1000毫米
case Units.Centimeters:
//LogManager.WriteLog("[单位检测] 确认为厘米单位,转换系数=100");
return 100.0; // 1米 = 100厘米
case Units.Meters:
//LogManager.WriteLog("[单位检测] 确认为米单位,转换系数=1");
return 1.0; // 1米 = 1米
case Units.Inches:
//LogManager.WriteLog("[单位检测] 确认为英寸单位,转换系数=39.37");
return 39.37; // 1米 = 39.37英寸
case Units.Feet:
//LogManager.WriteLog("[单位检测] 确认为英尺单位,转换系数=3.281");
return 3.281; // 1米 = 3.281英尺
case Units.Kilometers:
//LogManager.WriteLog("[单位检测] 确认为公里单位,转换系数=0.001");
return 0.001; // 1米 = 0.001公里
case Units.Micrometers:
//LogManager.WriteLog("[单位检测] 确认为微米单位,转换系数=1000000");
return 1000000.0; // 1米 = 1000000微米
case Units.Microinches:
//LogManager.WriteLog("[单位检测] 确认为微英寸单位,转换系数=39370078.74");
return 39370078.74; // 1米 = 39370078.74微英寸
case Units.Mils:
//LogManager.WriteLog("[单位检测] 确认为密尔单位,转换系数=39370.08");
return 39370.08; // 1米 = 39370.08密尔
case Units.Yards:
//LogManager.WriteLog("[单位检测] 确认为码单位,转换系数=1.094");
return 1.094; // 1米 = 1.094码
case Units.Miles:
//LogManager.WriteLog("[单位检测] 确认为英里单位,转换系数=0.000621");
return 0.000621; // 1米 = 0.000621英里
default:
//LogManager.WriteLog($"[单位检测] 未知单位类型: {units},使用默认米单位,转换系数=1");
return 1.0;
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[单位检测] API调用失败: {ex.Message}");
LogManager.WriteLog("[单位检测] 使用默认米单位,转换系数=1");
return 1.0;
}
}
/// <summary>
/// 根据路径点类型获取颜色
/// </summary>
public Color GetColorForPointType(PathPointType pointType)
{
switch (pointType)
{
case PathPointType.StartPoint:
return Color.Green; // 起点绿色
case PathPointType.EndPoint:
return Color.Red; // 终点红色
case PathPointType.WayPoint:
default:
return new Color(1.0, 0.0, 1.0); // 路径点洋红色RGB(1,0,1) - 高对比度避免与蓝色通道冲突
}
}
/// <summary>
/// 计算两点间距离
/// </summary>
private double CalculateDistance(Point3D point1, Point3D point2)
{
var dx = point1.X - point2.X;
var dy = point1.Y - point2.Y;
var dz = point1.Z - point2.Z;
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
/// <summary>
/// 请求视图刷新
/// </summary>
private void RequestViewRefresh()
{
try
{
if (Application.ActiveDocument?.ActiveView != null)
{
Application.ActiveDocument.ActiveView.RequestDelayedRedraw(ViewRedrawRequests.Render);
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[球体标记] 视图刷新失败: {ex.Message}");
}
}
#endregion
#region
/// <summary>
/// 清理资源
/// </summary>
public void CleanUp()
{
ClearAllMarkers();
}
#endregion
}
/// <summary>
/// 圆形标记数据结构
/// </summary>
public class CircleMarker
{
/// <summary>
/// 圆心坐标
/// </summary>
public Point3D Center { get; set; }
/// <summary>
/// 圆面法向量
/// </summary>
public Vector3D Normal { get; set; }
/// <summary>
/// 圆形半径
/// </summary>
public double Radius { get; set; }
/// <summary>
/// 圆形颜色
/// </summary>
public Color Color { get; set; }
/// <summary>
/// 不透明度 (1.0 = 完全不透明, 0.0 = 完全透明)
/// </summary>
public double Alpha { get; set; }
/// <summary>
/// 是否填充
/// </summary>
public bool Filled { get; set; }
/// <summary>
/// 路径点类型
/// </summary>
public PathPointType PointType { get; set; }
/// <summary>
/// 序号
/// </summary>
public int SequenceNumber { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedTime { get; set; }
public override string ToString()
{
return $"CircleMarker[序号={SequenceNumber}, 类型={PointType}, 中心=({Center.X:F2},{Center.Y:F2},{Center.Z:F2}), 半径={Radius:F2}]";
}
}
}