NavisworksTransport/CoordinateConverter.cs

425 lines
15 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 Autodesk.Navisworks.Api;
namespace NavisworksTransport
{
/// <summary>
/// 坐标转换工具类
/// 负责2D地图坐标与3D世界坐标之间的转换
/// </summary>
public class CoordinateConverter
{
private ChannelBounds _channelBounds;
private double _mapWidth;
private double _mapHeight;
private double _defaultElevation;
private double _zoomFactor = 1.0;
private double _offsetX = 0.0;
private double _offsetY = 0.0;
private const double _marginRatio = 0.1; // 10%边距
/// <summary>
/// 地图宽度(像素)
/// </summary>
public double MapWidth
{
get { return _mapWidth; }
set { _mapWidth = value; }
}
/// <summary>
/// 地图高度(像素)
/// </summary>
public double MapHeight
{
get { return _mapHeight; }
set { _mapHeight = value; }
}
/// <summary>
/// 通道边界信息
/// </summary>
public ChannelBounds ChannelBounds
{
get { return _channelBounds; }
set { _channelBounds = value; }
}
/// <summary>
/// 默认高程Z坐标
/// </summary>
public double DefaultElevation
{
get { return _defaultElevation; }
set { _defaultElevation = value; }
}
/// <summary>
/// 当前缩放因子
/// </summary>
public double ZoomFactor
{
get { return _zoomFactor; }
set { _zoomFactor = Math.Max(0.1, Math.Min(50.0, value)); } // 限制缩放范围
}
/// <summary>
/// X轴偏移量
/// </summary>
public double OffsetX
{
get { return _offsetX; }
set { _offsetX = value; }
}
/// <summary>
/// Y轴偏移量
/// </summary>
public double OffsetY
{
get { return _offsetY; }
set { _offsetY = value; }
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="channelBounds">通道边界信息</param>
/// <param name="mapWidth">地图窗口宽度</param>
/// <param name="mapHeight">地图窗口高度</param>
public CoordinateConverter(ChannelBounds channelBounds, double mapWidth, double mapHeight)
{
_channelBounds = channelBounds ?? throw new ArgumentNullException(nameof(channelBounds));
_mapWidth = mapWidth;
_mapHeight = mapHeight;
// 默认使用通道底部高程
_defaultElevation = channelBounds.MinPoint.Z + 0.1; // 稍微抬高一点避免在地面上
// 自动计算最佳视图
CalculateBestFitView();
}
/// <summary>
/// 计算最佳适应视图
/// </summary>
public void CalculateBestFitView()
{
if (_channelBounds == null) return;
// 计算世界坐标的实际尺寸
double worldWidth = _channelBounds.MaxPoint.X - _channelBounds.MinPoint.X;
double worldHeight = _channelBounds.MaxPoint.Y - _channelBounds.MinPoint.Y;
// 如果世界尺寸太小,设置最小显示尺寸
double minWorldSize = 1.0; // 最小1米
if (worldWidth < minWorldSize) worldWidth = minWorldSize;
if (worldHeight < minWorldSize) worldHeight = minWorldSize;
// 重置缩放和偏移(现在使用简化的坐标转换)
_zoomFactor = 1.0;
_offsetX = 0.0;
_offsetY = 0.0;
}
/// <summary>
/// 将2D地图坐标转换为3D世界坐标
/// </summary>
/// <param name="mapPoint">2D地图坐标像素坐标</param>
/// <returns>3D世界坐标</returns>
public Point3D MapToWorld(MapPoint2D mapPoint)
{
if (mapPoint == null)
throw new ArgumentNullException(nameof(mapPoint));
// 计算可用的地图尺寸(减去边距)
double availableWidth = _mapWidth * (1.0 - 2 * _marginRatio);
double availableHeight = _mapHeight * (1.0 - 2 * _marginRatio);
// 从地图坐标计算相对位置
double relativeX = (mapPoint.X - _mapWidth * _marginRatio) / availableWidth;
double relativeY = 1.0 - ((mapPoint.Y - _mapHeight * _marginRatio) / availableHeight); // Y轴翻转
// 转换为世界坐标
double worldX = _channelBounds.MinPoint.X + relativeX * (_channelBounds.MaxPoint.X - _channelBounds.MinPoint.X);
double worldY = _channelBounds.MinPoint.Y + relativeY * (_channelBounds.MaxPoint.Y - _channelBounds.MinPoint.Y);
double worldZ = _defaultElevation;
return new Point3D(worldX, worldY, worldZ);
}
/// <summary>
/// 将3D世界坐标转换为2D地图坐标
/// </summary>
/// <param name="worldPoint">3D世界坐标</param>
/// <returns>2D地图坐标像素坐标</returns>
public MapPoint2D WorldToMap(Point3D worldPoint)
{
if (worldPoint == null)
throw new ArgumentNullException(nameof(worldPoint));
// 计算世界坐标的范围
double worldWidth = _channelBounds.MaxPoint.X - _channelBounds.MinPoint.X;
double worldHeight = _channelBounds.MaxPoint.Y - _channelBounds.MinPoint.Y;
// 计算在通道范围内的相对位置0-1之间
double relativeX = (worldPoint.X - _channelBounds.MinPoint.X) / worldWidth;
double relativeY = (worldPoint.Y - _channelBounds.MinPoint.Y) / worldHeight;
// 计算可用的地图尺寸(减去边距)
double availableWidth = _mapWidth * (1.0 - 2 * _marginRatio);
double availableHeight = _mapHeight * (1.0 - 2 * _marginRatio);
// 转换为地图像素坐标
double mapX = relativeX * availableWidth + _mapWidth * _marginRatio;
double mapY = (1.0 - relativeY) * availableHeight + _mapHeight * _marginRatio; // Y轴翻转
// 确保坐标在地图范围内
mapX = Math.Max(0, Math.Min(_mapWidth, mapX));
mapY = Math.Max(0, Math.Min(_mapHeight, mapY));
return new MapPoint2D(mapX, mapY);
}
/// <summary>
/// 批量转换2D地图坐标为3D世界坐标
/// </summary>
/// <param name="mapPoints">2D地图坐标集合</param>
/// <returns>3D世界坐标集合</returns>
public List<Point3D> MapToWorldBatch(IEnumerable<MapPoint2D> mapPoints)
{
if (mapPoints == null)
throw new ArgumentNullException(nameof(mapPoints));
var worldPoints = new List<Point3D>();
foreach (var mapPoint in mapPoints)
{
worldPoints.Add(MapToWorld(mapPoint));
}
return worldPoints;
}
/// <summary>
/// 批量转换3D世界坐标为2D地图坐标
/// </summary>
/// <param name="worldPoints">3D世界坐标集合</param>
/// <returns>2D地图坐标集合</returns>
public List<MapPoint2D> WorldToMapBatch(IEnumerable<Point3D> worldPoints)
{
if (worldPoints == null)
throw new ArgumentNullException(nameof(worldPoints));
var mapPoints = new List<MapPoint2D>();
foreach (var worldPoint in worldPoints)
{
mapPoints.Add(WorldToMap(worldPoint));
}
return mapPoints;
}
/// <summary>
/// 检查2D地图坐标是否在有效范围内
/// </summary>
/// <param name="mapPoint">2D地图坐标</param>
/// <returns>是否在有效范围内</returns>
public bool IsValidMapPoint(MapPoint2D mapPoint)
{
if (mapPoint == null) return false;
return mapPoint.X >= 0 && mapPoint.X <= _mapWidth &&
mapPoint.Y >= 0 && mapPoint.Y <= _mapHeight;
}
/// <summary>
/// 检查3D世界坐标是否在通道范围内
/// </summary>
/// <param name="worldPoint">3D世界坐标</param>
/// <returns>是否在通道范围内</returns>
public bool IsValidWorldPoint(Point3D worldPoint)
{
if (worldPoint == null) return false;
return worldPoint.X >= _channelBounds.MinPoint.X && worldPoint.X <= _channelBounds.MaxPoint.X &&
worldPoint.Y >= _channelBounds.MinPoint.Y && worldPoint.Y <= _channelBounds.MaxPoint.Y;
}
/// <summary>
/// 计算地图缩放比例(世界单位/像素)
/// </summary>
/// <param name="scaleX">X轴缩放比例</param>
/// <param name="scaleY">Y轴缩放比例</param>
public void GetMapScale(out double scaleX, out double scaleY)
{
double worldWidth = _channelBounds.MaxPoint.X - _channelBounds.MinPoint.X;
double worldHeight = _channelBounds.MaxPoint.Y - _channelBounds.MinPoint.Y;
scaleX = worldWidth / _mapWidth;
scaleY = worldHeight / _mapHeight;
}
/// <summary>
/// 获取地图中心点的世界坐标
/// </summary>
/// <returns>地图中心点的3D世界坐标</returns>
public Point3D GetMapCenterInWorld()
{
var mapCenter = new MapPoint2D(_mapWidth / 2.0, _mapHeight / 2.0);
return MapToWorld(mapCenter);
}
/// <summary>
/// 根据通道高程调整3D坐标的Z值
/// </summary>
/// <param name="worldPoint">3D世界坐标</param>
/// <param name="elevationOffset">高程偏移量(相对于通道底部)</param>
/// <returns>调整后的3D坐标</returns>
public Point3D AdjustElevation(Point3D worldPoint, double elevationOffset = 0.0)
{
if (worldPoint == null)
throw new ArgumentNullException(nameof(worldPoint));
double adjustedZ = _channelBounds.MinPoint.Z + elevationOffset;
return new Point3D(worldPoint.X, worldPoint.Y, adjustedZ);
}
/// <summary>
/// 计算两个地图点之间的像素距离
/// </summary>
/// <param name="point1">第一个地图点</param>
/// <param name="point2">第二个地图点</param>
/// <returns>像素距离</returns>
public double CalculateMapDistance(MapPoint2D point1, MapPoint2D point2)
{
if (point1 == null || point2 == null)
throw new ArgumentNullException("地图点不能为空");
double dx = point2.X - point1.X;
double dy = point2.Y - point1.Y;
return Math.Sqrt(dx * dx + dy * dy);
}
/// <summary>
/// 计算两个世界坐标点之间的距离
/// </summary>
/// <param name="point1">第一个世界坐标点</param>
/// <param name="point2">第二个世界坐标点</param>
/// <returns>世界距离(米)</returns>
public double CalculateWorldDistance(Point3D point1, Point3D point2)
{
if (point1 == null || point2 == null)
throw new ArgumentNullException("世界坐标点不能为空");
double dx = point2.X - point1.X;
double dy = point2.Y - point1.Y;
double dz = point2.Z - point1.Z;
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
/// <summary>
/// 更新地图尺寸
/// </summary>
/// <param name="newWidth">新的地图宽度</param>
/// <param name="newHeight">新的地图高度</param>
public void UpdateMapSize(double newWidth, double newHeight)
{
_mapWidth = newWidth;
_mapHeight = newHeight;
}
/// <summary>
/// 更新通道边界
/// </summary>
/// <param name="newChannelBounds">新的通道边界</param>
public void UpdateChannelBounds(ChannelBounds newChannelBounds)
{
_channelBounds = newChannelBounds ?? throw new ArgumentNullException(nameof(newChannelBounds));
// 更新默认高程
_defaultElevation = _channelBounds.MinPoint.Z + 0.1;
}
/// <summary>
/// 获取转换器信息摘要
/// </summary>
/// <returns>转换器信息字符串</returns>
public override string ToString()
{
double scaleX, scaleY;
GetMapScale(out scaleX, out scaleY);
return $"坐标转换器 - 地图尺寸:{_mapWidth}x{_mapHeight}, " +
$"世界范围:({_channelBounds.MinPoint.X:F2},{_channelBounds.MinPoint.Y:F2}) - " +
$"({_channelBounds.MaxPoint.X:F2},{_channelBounds.MaxPoint.Y:F2}), " +
$"缩放比例:{scaleX:F4},{scaleY:F4}";
}
/// <summary>
/// 放大视图
/// </summary>
/// <param name="factor">放大倍数</param>
/// <param name="centerPoint">放大中心点(地图坐标)</param>
public void ZoomIn(double factor = 1.5, MapPoint2D centerPoint = null)
{
if (centerPoint == null)
{
centerPoint = new MapPoint2D(_mapWidth / 2.0, _mapHeight / 2.0);
}
// 保存当前中心点在世界坐标中的位置
var worldCenter = MapToWorld(centerPoint);
// 应用缩放
_zoomFactor *= factor;
_zoomFactor = Math.Min(50.0, _zoomFactor); // 限制最大缩放
// 重新计算偏移以保持中心点位置
var newMapCenter = WorldToMap(worldCenter);
_offsetX += centerPoint.X - newMapCenter.X;
_offsetY += centerPoint.Y - newMapCenter.Y;
}
/// <summary>
/// 缩小视图
/// </summary>
/// <param name="factor">缩小倍数</param>
/// <param name="centerPoint">缩小中心点(地图坐标)</param>
public void ZoomOut(double factor = 1.5, MapPoint2D centerPoint = null)
{
if (centerPoint == null)
{
centerPoint = new MapPoint2D(_mapWidth / 2.0, _mapHeight / 2.0);
}
// 保存当前中心点在世界坐标中的位置
var worldCenter = MapToWorld(centerPoint);
// 应用缩放
_zoomFactor /= factor;
_zoomFactor = Math.Max(0.1, _zoomFactor); // 限制最小缩放
// 重新计算偏移以保持中心点位置
var newMapCenter = WorldToMap(worldCenter);
_offsetX += centerPoint.X - newMapCenter.X;
_offsetY += centerPoint.Y - newMapCenter.Y;
}
/// <summary>
/// 重置视图到最佳适应
/// </summary>
public void ResetView()
{
CalculateBestFitView();
}
/// <summary>
/// 平移视图
/// </summary>
/// <param name="deltaX">X轴移动距离像素</param>
/// <param name="deltaY">Y轴移动距离像素</param>
public void Pan(double deltaX, double deltaY)
{
_offsetX += deltaX;
_offsetY += deltaY;
}
}
}