NavisworksTransport/PathPointRenderPlugin.cs
2025-06-19 20:21:26 +08:00

674 lines
27 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;
LogManager.WriteLog("[RenderPlugin] PathPointRenderPlugin构造函数被调用");
}
/// <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)
{
LogManager.WriteLog($"[Render入口] ✓ Render方法被调用, IsEnabled={_isEnabled}");
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"}");
// 详细的视图和相机诊断信息
try
{
LogManager.WriteLog($"[视图诊断] 开始获取视图信息");
// 尝试获取第一个模型的信息
var firstModel = activeDoc.Models.First();
LogManager.WriteLog($"[视图诊断] 第一个模型文件名: {firstModel.FileName}");
// 尝试获取根项目信息
var rootItem = firstModel.RootItem;
if (rootItem != null)
{
LogManager.WriteLog($"[视图诊断] 根项目显示名: {rootItem.DisplayName}");
LogManager.WriteLog($"[视图诊断] 根项目子项数量: {rootItem.Children.Count()}");
}
}
catch (Exception viewEx)
{
LogManager.WriteLog($"[视图诊断] 获取视图信息失败: {viewEx.Message}");
}
// 使用BeginModelContext确保正确的渲染上下文
graphics.BeginModelContext();
LogManager.WriteLog($"[Graphics状态] BeginModelContext完成");
// 超大超明显的测试线段 - 覆盖整个模型范围
graphics.Color(Color.Red, 0.0);
graphics.LineWidth(50); // 超粗线条
LogManager.WriteLog($"[Graphics状态] Color和LineWidth设置完成");
// 视线前方测试使用正确的Navisworks API获取当前视点
try
{
LogManager.WriteLog($"[视线测试] 开始获取当前视点信息");
// 使用正确的API获取当前视点
var document = Application.ActiveDocument;
var currentViewpoint = document.CurrentViewpoint.CreateCopy();
// 获取相机位置和旋转
var cameraPosition = currentViewpoint.Position;
var cameraRotation = currentViewpoint.Rotation;
LogManager.WriteLog($"[视线测试] 相机位置: ({cameraPosition.X:F2}, {cameraPosition.Y:F2}, {cameraPosition.Z:F2})");
LogManager.WriteLog($"[视线测试] 相机旋转信息获取成功");
// 使用最简单的方法:在相机位置周围绘制球体
graphics.Color(Color.Red, 0.0); // 完全不透明红色
// 在相机位置的前、后、左、右、上、下绘制红球
var offsets = new[]
{
new Vector3D(0, 0, -50), // 前方
new Vector3D(0, 0, 50), // 后方
new Vector3D(-50, 0, 0), // 左侧
new Vector3D(50, 0, 0), // 右侧
new Vector3D(0, 50, 0), // 上方
new Vector3D(0, -50, 0), // 下方
};
foreach (var offset in offsets)
{
var ballPosition = new Point3D(
cameraPosition.X + offset.X,
cameraPosition.Y + offset.Y,
cameraPosition.Z + offset.Z
);
graphics.Sphere(ballPosition, 10.0); // 10单位半径
LogManager.WriteLog($"[视线测试] 红球位置: ({ballPosition.X:F2}, {ballPosition.Y:F2}, {ballPosition.Z:F2})");
}
LogManager.WriteLog($"[视线测试] ✓ 完成在相机周围绘制 {offsets.Length} 个红球");
}
catch (Exception viewEx)
{
LogManager.WriteLog($"[视线测试] ❌ 视线测试异常: {viewEx.Message}");
// 备用方案:简单位置绘制
LogManager.WriteLog($"[视线测试] 使用备用方案:固定位置绘制");
graphics.Color(Color.Red, 0.0);
var backupPositions = new[]
{
new Point3D(0, 0, 0), // 原点
new Point3D(100, 100, 100), // 正方向
new Point3D(-100, -100, -100), // 负方向
};
foreach (var pos in backupPositions)
{
graphics.Sphere(pos, 20.0);
LogManager.WriteLog($"[视线测试] 备用红球: ({pos.X}, {pos.Y}, {pos.Z})");
}
}
var sphereCenter = new Point3D(0, 0, 0); // 原点位置
var sphereRadius = 50000.0; // 超大半径50米
try
{
graphics.Sphere(sphereCenter, sphereRadius);
LogManager.WriteLog($"[球体测试] ✓ 绘制超大红色球体: 中心(0,0,0), 半径={sphereRadius}");
}
catch (Exception sphereEx)
{
LogManager.WriteLog($"[球体测试] ❌ Sphere调用异常: {sphereEx.Message}");
}
// 再在模型区域绘制一个球体
var modelCenter = new Point3D(27000.0, -10000.0, 1000.0);
var modelRadius = 5000.0; // 5米半径
try
{
graphics.Sphere(modelCenter, modelRadius);
LogManager.WriteLog($"[球体测试] ✓ 绘制模型区域球体: 中心({modelCenter.X}, {modelCenter.Y}, {modelCenter.Z}), 半径={modelRadius}");
}
catch (Exception modelSphereEx)
{
LogManager.WriteLog($"[球体测试] ❌ 模型球体调用异常: {modelSphereEx.Message}");
}
// 先绘制测试大圆形 - 验证Graphics.Circle API
var testCenter = new Point3D(19082.41, -18248.14, 4037.26);
var testRadius = 200.0; // 200英寸大圆应该非常明显
var testColor = new Color(1.0, 0.0, 0.0); // 红色,高对比度
graphics.Color(testColor, 0.0); // 完全不透明红色
graphics.Circle(testCenter, new Vector3D(0, 0, 1), testRadius, true); // 实心圆
LogManager.WriteLog($"[测试圆形] 绘制测试大圆: 中心({testCenter.X:F2}, {testCenter.Y:F2}, {testCenter.Z:F2}), 半径={testRadius:F2}, 颜色=红色");
lock (_lockObject)
{
if (_circleMarkers.Count > 0)
{
LogManager.WriteLog($"[圆形渲染] 开始渲染 {_circleMarkers.Count} 个圆形标记");
}
// 遍历所有圆形标记并绘制
foreach (var marker in _circleMarkers)
{
// 设置颜色和透明度
graphics.Color(marker.Color, marker.Transparency);
// 绘制圆形标记
graphics.Circle(marker.Center, marker.Normal, marker.Radius, marker.Filled);
LogManager.WriteLog($"[圆形渲染] 绘制圆形: 中心({marker.Center.X:F2}, {marker.Center.Y:F2}, {marker.Center.Z:F2}), 半径={marker.Radius:F2}, 序号={marker.SequenceNumber}");
}
}
// 结束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>
/// 2D覆盖层渲染方法
/// </summary>
/// <param name="view">当前视图</param>
/// <param name="graphics">图形上下文</param>
public override void OverlayRender(View view, Graphics graphics)
{
LogManager.WriteLog($"[OverlayRender入口] ✓ OverlayRender方法被调用, IsEnabled={_isEnabled}");
if (!_isEnabled) return;
try
{
// 首先检查文档和模型状态
var activeDoc = Application.ActiveDocument;
LogManager.WriteLog($"[OverlayRender] ActiveDocument: {activeDoc?.GetType().Name ?? "null"}");
if (activeDoc == null)
{
LogManager.WriteLog($"[OverlayRender] ❌ 没有活动文档跳过2D渲染");
return;
}
if (activeDoc.Models == null || activeDoc.Models.Count == 0)
{
LogManager.WriteLog($"[OverlayRender] ❌ 没有加载的模型跳过2D渲染");
return;
}
LogManager.WriteLog($"[OverlayRender] ✓ 文档正常开始2D渲染");
// 检查Graphics对象状态
LogManager.WriteLog($"[OverlayRender] Graphics对象类型: {graphics?.GetType().Name}");
LogManager.WriteLog($"[OverlayRender] View对象: {view?.GetType().Name ?? "null"}");
// 使用最简单的API测试
var red = Color.FromByteRGB(255, 0, 0);
graphics.Color(red, 0.0); // 完全不透明的红色
LogManager.WriteLog($"[OverlayRender] 设置颜色: R=255, G=0, B=0, Alpha=0.0");
graphics.LineWidth(20); // 很粗的线
LogManager.WriteLog($"[OverlayRender] 设置线宽: 20");
// 绘制一条从屏幕左上角到右下角的对角线
try
{
var startPoint = new Point3D(0, 0, 0);
var endPoint = new Point3D(800, 600, 0);
graphics.Line(startPoint, endPoint);
LogManager.WriteLog($"[OverlayRender] ✓ 绘制对角线: (0,0) 到 (800,600)");
}
catch (Exception lineEx)
{
LogManager.WriteLog($"[OverlayRender] ❌ Line调用异常: {lineEx.Message}");
}
// 尝试绘制一个简单的点
try
{
graphics.Point(new Point3D(100, 100, 0));
LogManager.WriteLog($"[OverlayRender] ✓ 绘制点: (100,100)");
}
catch (Exception pointEx)
{
LogManager.WriteLog($"[OverlayRender] ❌ Point调用异常: {pointEx.Message}");
}
// 尝试强制刷新
try
{
if (view != null)
{
view.RequestDelayedRedraw(ViewRedrawRequests.Render);
LogManager.WriteLog($"[OverlayRender] ✓ 请求覆盖层重绘");
}
}
catch (Exception refreshEx)
{
LogManager.WriteLog($"[OverlayRender] ❌ 重绘请求异常: {refreshEx.Message}");
}
LogManager.WriteLog($"[OverlayRender] === 2D覆盖层绘制完成 ===");
}
catch (Exception ex)
{
LogManager.WriteLog($"[OverlayRender] 渲染错误: {ex.Message}");
LogManager.WriteLog($"[OverlayRender] 异常堆栈: {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),
Transparency = 0.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);
}
}
#region
/// <summary>
/// 根据路径点类型和真实文档单位获取适当的半径
/// 目标路径点半径为0.5米物理尺寸,起点/终点为0.8米物理尺寸
/// </summary>
private double GetRadiusForPointType(PathPointType pointType)
{
// 基础半径(米为单位)
double baseRadiusInMeters = pointType == PathPointType.WayPoint ? 0.5 : 0.8;
// 获取真实文档单位转换系数
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>
private 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>
/// 透明度 (0.0-1.0)
/// </summary>
public double Transparency { 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}]";
}
}
}