using OpenTK.GLControl;
|
using OpenTK.Mathematics;
|
|
namespace Yw.WinFrmUI.Hydro
|
{
|
/// <summary>
|
/// 正交伪3d视图辅助类
|
/// </summary>
|
public class Ortho2dViewController
|
{
|
/// <summary>
|
///
|
/// </summary>
|
public Ortho2dViewController() { }
|
|
/// <summary>
|
/// 初始化
|
/// </summary>
|
public void Initial(List<PointL3d> coordinates)
|
{
|
_coordinates = coordinates;
|
}
|
|
|
|
/// <summary>
|
/// 旋转
|
/// </summary>
|
public Vector2 Rotation
|
{
|
get { return _rotation; }
|
private set { _rotation = value; }
|
}
|
private Vector2 _rotation = new Vector2(-45, 20);
|
|
/// <summary>
|
/// 平移
|
/// </summary>
|
public Vector2 Offset
|
{
|
get { return _offset; }
|
private set { _offset = value; }
|
}
|
private Vector2 _offset = Vector2.Zero;
|
|
/// <summary>
|
/// 缩放
|
/// </summary>
|
public float Zoom
|
{
|
get { return _zoom; }
|
private set { _zoom = value; }
|
}
|
private float _zoom = 1f;
|
|
/// <summary>
|
/// 模型矩阵
|
/// </summary>
|
public Matrix4 ModelMatrix
|
{
|
get
|
{
|
return Matrix4.CreateTranslation(Offset.X, Offset.Y, 0) *
|
Matrix4.CreateRotationX(this.Rotation.Y) *
|
Matrix4.CreateRotationY(this.Rotation.X);
|
}
|
}
|
|
// 矩阵缓存
|
public Matrix4 ViewMatrix
|
{
|
get { return _viewMatrix; }
|
private set { _viewMatrix = value; }
|
}
|
private Matrix4 _viewMatrix;
|
|
/// <summary>
|
/// 投影矩阵
|
/// </summary>
|
public Matrix4 ProjectionMatrix
|
{
|
get { return _projectionMatrix; }
|
private set { _projectionMatrix = value; }
|
}
|
private Matrix4 _projectionMatrix;
|
|
|
private List<PointL3d> _coordinates; // 坐标数据
|
private float _baseSize = 10f;//基础尺寸
|
private const float _rotationSpeed = 0.01f;//旋转系数
|
private const float _panSpeed = 1f;//平移系数
|
private Point? _lastMousePos;//鼠标最后位置
|
private int _width = 1;//宽度
|
private int _height = 1;//高度
|
|
|
|
/// <summary>
|
/// 处理鼠标按下
|
/// </summary>
|
public void HandleMouseDown(MouseEventArgs e)
|
{
|
_lastMousePos = e.Location;
|
}
|
|
/// <summary>
|
/// 处理鼠标移动
|
/// </summary>
|
public void HandleMouseMove(MouseEventArgs e, Control control)
|
{
|
if (!_lastMousePos.HasValue)
|
{
|
return;
|
}
|
|
Point delta = new Point(
|
e.X - _lastMousePos.Value.X,
|
e.Y - _lastMousePos.Value.Y
|
);
|
|
if (e.Button == MouseButtons.Right)
|
{
|
_rotation += new Vector2(
|
delta.Y * _rotationSpeed,
|
delta.X * _rotationSpeed
|
);
|
_rotation.Y = MathHelper.Clamp(Rotation.Y, -85, 85);
|
}
|
else if (e.Button == MouseButtons.Left)
|
{
|
float zoomFactor = 1 / Zoom;
|
_offset += new Vector2(
|
delta.X * _panSpeed * zoomFactor,
|
-delta.Y * _panSpeed * zoomFactor
|
);
|
}
|
|
_lastMousePos = e.Location;
|
control.Invalidate();
|
}
|
|
/// <summary>
|
/// 处理鼠标弹起
|
/// </summary>
|
public void HandleMouseUp(MouseEventArgs e)
|
{
|
_lastMousePos = null;
|
}
|
|
/// <summary>
|
/// 处理鼠标滚轮
|
/// </summary>
|
public void HandleMouseWheel(MouseEventArgs e)
|
{
|
_zoom *= e.Delta > 0 ? 0.9f : 1.1f;
|
// _zoom = MathHelper.Clamp(_zoom, 0.5f, 5f);
|
UpdateProjection(_width, _height);
|
}
|
|
/// <summary>
|
/// 更新投影
|
/// </summary>
|
public void UpdateProjection(int width, int height)
|
{
|
if (_coordinates == null || _coordinates.Count < 1)
|
{
|
return;
|
}
|
_width = width;
|
_height = height;
|
// 计算旋转后的包围盒
|
var rotationMatrix = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(this.Rotation.Y)) *
|
Matrix4.CreateRotationY(MathHelper.DegreesToRadians(this.Rotation.X));
|
var quatenion = new Quaternion(this.Rotation.X, this.Rotation.Y, 0);
|
var rotatedMin = new Vector3(float.MaxValue);
|
var rotatedMax = new Vector3(float.MinValue);
|
foreach (var coor in _coordinates)
|
{
|
var vnode = new Vector3(coor.X, coor.Y, coor.Z);
|
|
Vector3 rotatedPos = Vector3.Transform(vnode, quatenion);
|
rotatedMin = Vector3.ComponentMin(rotatedMin, rotatedPos);
|
rotatedMax = Vector3.ComponentMax(rotatedMax, rotatedPos);
|
}
|
|
// 计算动态裁剪面
|
float margin = 1.5f;
|
float zSpan = rotatedMax.Z - rotatedMin.Z;
|
float zNear = rotatedMin.Z - zSpan * (margin - 1);
|
float zFar = rotatedMax.Z + zSpan * (margin - 1);
|
|
// 约束最小深度范围
|
if (zFar - zNear < 5f)
|
{
|
zNear = -2.5f;
|
zFar = 2.5f;
|
}
|
|
// 更新投影矩阵
|
float aspect = (float)width / height;
|
ProjectionMatrix = Matrix4.CreateOrthographic(
|
_baseSize * aspect / _zoom,
|
_baseSize / _zoom,
|
zNear,
|
zFar
|
);
|
|
// 更新视图矩阵
|
ViewMatrix = Matrix4.LookAt(
|
new Vector3(0, 0, 10 + _baseSize * 0.5f),
|
new Vector3(_offset.X, _offset.Y, 0),
|
Vector3.UnitZ
|
);
|
}
|
|
//自适应
|
public void FitToBounds(int width, int height)
|
{
|
var box = CalculateBoundingBox();
|
if (box == null)
|
{
|
return;
|
}
|
_width = width;
|
_height = height;
|
var size = box.Max - box.Min;
|
_baseSize = Math.Max(size.X, Math.Max(size.Y, size.Z)) * 1.2f;
|
_offset = new Vector2(
|
-(box.Min.X + size.X / 2),
|
-(box.Min.Y + size.Y / 2)
|
);
|
|
UpdateProjection(_width, _height);
|
}
|
|
//计算包围盒
|
private BoundingBoxL3d CalculateBoundingBox()
|
{
|
if (_coordinates == null || _coordinates.Count < 1)
|
{
|
return default;
|
}
|
PointL3d min = new PointL3d(float.MaxValue);
|
PointL3d max = new PointL3d(float.MinValue);
|
foreach (var coor in _coordinates)
|
{
|
min = PointL3d.ComponentMin(min, coor);
|
max = PointL3d.ComponentMax(max, coor);
|
}
|
return new BoundingBoxL3d(min, max);
|
}
|
|
|
|
|
|
|
|
|
|
}
|
}
|