939 lines
29 KiB
C#
939 lines
29 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Drawing.Drawing2D;
|
||
using System.Linq;
|
||
using System.Runtime.Serialization.Formatters.Binary;
|
||
using System.Runtime.Serialization;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using CanFly.Canvas.Helper;
|
||
using Newtonsoft.Json;
|
||
using System.Diagnostics;
|
||
using System.Net.NetworkInformation;
|
||
using System.Drawing;
|
||
|
||
|
||
namespace CanFly.Canvas.Shape
|
||
{
|
||
[Serializable]
|
||
public class FlyShape
|
||
{
|
||
private const float DFT_VTX_EPSILON = 4f;
|
||
private float _epsilon = DFT_VTX_EPSILON;
|
||
|
||
public float LineWidth { get; set; } = 2f;
|
||
|
||
|
||
#region Shape颜色
|
||
|
||
#region drawing
|
||
public Color line_color = Color.FromArgb(128, 0, 255, 0);
|
||
public Color fill_color = Color.FromArgb(64, 0, 0, 0);
|
||
public Color vertex_fill_color = Color.FromArgb(255, 0, 255, 0);
|
||
#endregion
|
||
|
||
#region selecting / hovering
|
||
public Color select_line_color = Color.FromArgb(255, 255, 255, 255);
|
||
public Color select_fill_color = Color.FromArgb(64, 0, 255, 0);
|
||
public Color hvertex_fill_color = Color.FromArgb(255, 255, 255, 255);
|
||
#endregion
|
||
|
||
#endregion
|
||
|
||
|
||
private PointTypeEnum point_type = PointTypeEnum.ROUND;
|
||
private float point_size = 8.0f;
|
||
|
||
private float _scale = 1.0f;
|
||
private float scale
|
||
{
|
||
get
|
||
{
|
||
return _scale;
|
||
}
|
||
set
|
||
{
|
||
_scale = value;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
private ShapeTypeEnum _shape_type;
|
||
private Matrix _matrix = new Matrix();
|
||
|
||
|
||
|
||
|
||
public ShapeTypeEnum ShapeType
|
||
{
|
||
get => _shape_type;
|
||
set { _shape_type = value; }
|
||
}
|
||
|
||
|
||
public string label = "";
|
||
public int? group_id = null;
|
||
private List<PointF> _points
|
||
{
|
||
get;
|
||
set;
|
||
} = new List<PointF>();
|
||
|
||
public List<PointF> Points
|
||
{
|
||
get { return _points; }
|
||
set
|
||
{
|
||
this._points = value;
|
||
}
|
||
}
|
||
|
||
|
||
private List<PointF> _pointsRaw = new List<PointF>();
|
||
|
||
|
||
/// <summary>
|
||
/// 辅助节点
|
||
/// </summary>
|
||
public List<PointF> GuidePoints = new List<PointF>();
|
||
|
||
public float _currentRotateAngle;
|
||
private bool _isRotating = false;
|
||
|
||
|
||
public List<int> point_labels = new List<int>();
|
||
|
||
|
||
private ShapeTypeEnum _shape_type_raw;
|
||
|
||
|
||
/// <summary>
|
||
/// 是否填充多边形 使用:select_fill_color 或 fill_color 填充
|
||
/// </summary>
|
||
public bool fill = false;
|
||
|
||
|
||
public bool Selected { get; set; } = false;
|
||
public object? flags;
|
||
public string description = "";
|
||
private List<object> other_data = new List<object>();
|
||
|
||
|
||
private int _highlightIndex = -1;
|
||
private HighlightModeEnum _highlightMode = HighlightModeEnum.NEAR_VERTEX;
|
||
|
||
|
||
private Dictionary<HighlightModeEnum, HighlightSetting> _highlightSettings = new Dictionary<HighlightModeEnum, HighlightSetting>()
|
||
{
|
||
{ HighlightModeEnum.NEAR_VERTEX,new HighlightSetting(4,PointTypeEnum.ROUND)},
|
||
{ HighlightModeEnum.MOVE_VERTEX,new HighlightSetting(1.5f,PointTypeEnum.SQUARE)},
|
||
};
|
||
|
||
|
||
private bool _closed = false;
|
||
private Color _vertex_fill_color;
|
||
|
||
|
||
/// <summary>
|
||
/// 当图形是Line时,是否绘制辅助矩形框
|
||
/// </summary>
|
||
public bool IsDrawLineVirtualRect { get; set; } = false;
|
||
/// <summary>
|
||
/// 画Line时辅助矩形的宽度
|
||
/// </summary>
|
||
public float LineVirtualRectWidth = 40;
|
||
public PointF[] LineVirtualRectPoints = new PointF[4];
|
||
|
||
|
||
public FlyShape()
|
||
{
|
||
|
||
}
|
||
|
||
|
||
|
||
private PointF ScalePoint(PointF point)
|
||
{
|
||
return point;
|
||
|
||
//return new PointF(
|
||
// (float)(point.X * scale),
|
||
// (float)(point.Y * scale));
|
||
}
|
||
|
||
|
||
|
||
|
||
public void Close()
|
||
{
|
||
this._closed = true;
|
||
}
|
||
|
||
|
||
public void AddPoint(PointF point, int label = 1)
|
||
{
|
||
if (_points != null && _points.Count > 0 && point.Equals(_points.ElementAt(0)))
|
||
{
|
||
Close();
|
||
}
|
||
else
|
||
{
|
||
if (_points.Count > 0 && this[-1].Equals(point))
|
||
{
|
||
return;
|
||
}
|
||
_points.Add(point);
|
||
point_labels.Add(label);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
public bool CanAddPoint()
|
||
{
|
||
return ShapeType == ShapeTypeEnum.Polygon
|
||
|| ShapeType == ShapeTypeEnum.LineStrip;
|
||
}
|
||
|
||
|
||
|
||
public PointF? PopPoint()
|
||
{
|
||
if (_points != null && _points.Count > 0)
|
||
{
|
||
if (point_labels != null && point_labels.Count > 0)
|
||
{
|
||
point_labels.RemoveAt(point_labels.Count - 1);
|
||
}
|
||
|
||
PointF lastPoint = _points[_points.Count - 1];
|
||
_points.RemoveAt(_points.Count - 1);
|
||
return lastPoint;
|
||
}
|
||
return null;
|
||
|
||
}
|
||
|
||
|
||
|
||
public void InsertPoint(int i, PointF point, int label = 1)
|
||
{
|
||
_points.Insert(i, point);
|
||
point_labels.Insert(i, label);
|
||
}
|
||
|
||
|
||
public void RemovePoint(int i)
|
||
{
|
||
if (!CanAddPoint())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (ShapeType == ShapeTypeEnum.Polygon && _points.Count <= 3)
|
||
{
|
||
return;
|
||
}
|
||
else if (ShapeType == ShapeTypeEnum.LineStrip && _points.Count <= 2)
|
||
{
|
||
return;
|
||
}
|
||
|
||
|
||
_points.RemoveAt(_points.Count - 1);
|
||
point_labels.RemoveAt(point_labels.Count - 1);
|
||
}
|
||
|
||
|
||
|
||
public bool IsClosed() => _closed;
|
||
|
||
|
||
public void SetOpen() { _closed = false; }
|
||
|
||
|
||
#region 矩形辅助函数
|
||
|
||
/// <summary>
|
||
/// 矩形模式下,选中的点索引
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
private int _rectSelectedVertex = -1;
|
||
[JsonIgnore]
|
||
private PointF _rectSelectedMoveVertex;
|
||
[JsonIgnore]
|
||
private PointF _rectCenterPoint;
|
||
|
||
[JsonIgnore]
|
||
private bool isVertexMoving;
|
||
|
||
/// <summary>
|
||
/// 正在移动节点
|
||
/// </summary>
|
||
[JsonIgnore]
|
||
public bool IsVertexMoving
|
||
{
|
||
get
|
||
{
|
||
return isVertexMoving;
|
||
}
|
||
internal set
|
||
{
|
||
//float centerX = (_points[0].X + _points[2].X) / 2f;
|
||
//float centerY = (_points[0].Y + _points[2].Y) / 2f;
|
||
//_rectCenterVertex = new PointF(centerX, centerY);
|
||
isVertexMoving = value;
|
||
}
|
||
}
|
||
//private PointF[] TransformPoints(List<PointF> points, PointF center, float angle)
|
||
//{
|
||
// PointF[] ptsArray = points.ToArray();
|
||
// using (Matrix matrix = new Matrix())
|
||
// {
|
||
// matrix.RotateAt(angle, center);
|
||
// matrix.TransformPoints(ptsArray);
|
||
// }
|
||
// return ptsArray;
|
||
//}
|
||
//GraphicsPath vrtx_path = new GraphicsPath();
|
||
|
||
#endregion
|
||
|
||
|
||
public void Paint(Graphics painter)
|
||
{
|
||
if (_points == null || _points.Count == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Color color = Selected ? select_line_color : line_color;
|
||
using Pen pen = new Pen(color, LineWidth);
|
||
|
||
// Create paths
|
||
GraphicsPath line_path = new GraphicsPath();
|
||
GraphicsPath vrtx_path = new GraphicsPath();
|
||
GraphicsPath guide_vrtx_path = new GraphicsPath();
|
||
|
||
switch (ShapeType)
|
||
{
|
||
//case ShapeTypeEnum.Rectangle:
|
||
// {
|
||
// if (_points.Count == 2)
|
||
// {
|
||
// float centerX = (_points[0].X + _points[1].X) / 2f;
|
||
// float centerY = (_points[0].Y + _points[1].Y) / 2f;
|
||
// _rectCenterPoint = new PointF(centerX, centerY);
|
||
|
||
|
||
// line_path.StartFigure();
|
||
|
||
// if (_points[1].X < _points[0].X)
|
||
// {
|
||
// _points[1] = new PointF(_points[0].X + LineWidth / 2f, _points[1].Y);
|
||
// }
|
||
// if (_points[1].Y < _points[0].Y)
|
||
// {
|
||
// _points[1] = new PointF(_points[1].X, _points[0].Y + LineWidth / 2f);
|
||
// }
|
||
|
||
// //float x = Math.Min(_points[0].X, _points[1].X);
|
||
// //float y = Math.Min(_points[0].Y, _points[1].Y);
|
||
|
||
// float w = Math.Abs(ScalePoint(_points[1]).X - ScalePoint(_points[0]).X);
|
||
// float h = Math.Abs(ScalePoint(_points[1]).Y - ScalePoint(_points[0]).Y);
|
||
|
||
// RectangleF drawRect = new(new PointF(_points[0].X, _points[0].Y), new SizeF(w, h));
|
||
|
||
// bool bRotated = false;
|
||
// NomalizeRotateAngle();
|
||
|
||
// Matrix oMatrix = null;
|
||
|
||
// if (_currentRotateAngle > 0)
|
||
// {
|
||
// // Create rotation matrix
|
||
// oMatrix = new Matrix();
|
||
// oMatrix.RotateAt(_currentRotateAngle, _rectCenterPoint, MatrixOrder.Append);
|
||
// painter.Transform = oMatrix;
|
||
// bRotated = true;
|
||
// }
|
||
// //Store rectangle region
|
||
// //Region _drawRectRegion = new Region(drawRect);
|
||
// if (oMatrix != null)
|
||
// line_path.Transform(oMatrix);
|
||
|
||
|
||
// line_path.AddRectangle(drawRect);
|
||
|
||
|
||
// // Reset transform
|
||
// if (bRotated)
|
||
// {
|
||
// bRotated = false;
|
||
// painter.ResetTransform();
|
||
// }
|
||
// //_matrix.Reset();
|
||
// //_matrix.RotateAt(_currentRotateAngle, new PointF(
|
||
// // (_points[0].X + _points[1].X) / 2,
|
||
// // (_points[0].Y + _points[1].Y) / 2));
|
||
// //line_path.Transform(_matrix);
|
||
|
||
|
||
// //line_path.AddPolygon(_pointsRaw.ToArray());
|
||
|
||
// }
|
||
|
||
// if (_regionVertex.Length != _points.Count)
|
||
// {
|
||
// _regionVertex = new Region[_points.Count];
|
||
// }
|
||
|
||
|
||
// for (int i = 0; i < _points.Count; i++)
|
||
// {
|
||
// DrawVertex(vrtx_path, i);
|
||
// }
|
||
|
||
// vrtx_path.Transform(_matrix);
|
||
|
||
// }
|
||
|
||
// break;
|
||
case ShapeTypeEnum.Rectangle:
|
||
{
|
||
if (_points.Count == 2)
|
||
{
|
||
float centerX = (_points[0].X + _points[1].X) / 2f;
|
||
float centerY = (_points[0].Y + _points[1].Y) / 2f;
|
||
_rectCenterPoint = new PointF(centerX, centerY);
|
||
|
||
line_path.StartFigure();
|
||
|
||
if (_points[1].X < _points[0].X)
|
||
{
|
||
_points[1] = new PointF(_points[0].X + LineWidth / 2f, _points[1].Y);
|
||
}
|
||
if (_points[1].Y < _points[0].Y)
|
||
{
|
||
_points[1] = new PointF(_points[1].X, _points[0].Y + LineWidth / 2f);
|
||
}
|
||
|
||
float w = Math.Abs(ScalePoint(_points[1]).X - ScalePoint(_points[0]).X);
|
||
float h = Math.Abs(ScalePoint(_points[1]).Y - ScalePoint(_points[0]).Y);
|
||
|
||
RectangleF drawRect = new(new PointF(_points[0].X, _points[0].Y), new SizeF(w, h));
|
||
|
||
line_path.AddRectangle(drawRect);
|
||
|
||
_matrix.Reset();
|
||
_matrix.RotateAt(_currentRotateAngle, _rectCenterPoint); // 使用更新后的旋转角度
|
||
line_path.Transform(_matrix);
|
||
}
|
||
|
||
if (_regionVertex.Length != _points.Count)
|
||
{
|
||
_regionVertex = new Region[_points.Count];
|
||
}
|
||
|
||
for (int i = 0; i < _points.Count; i++)
|
||
{
|
||
DrawVertex1(vrtx_path, i);
|
||
}
|
||
|
||
vrtx_path.Transform(_matrix);
|
||
|
||
|
||
}
|
||
break;
|
||
case ShapeTypeEnum.Circle:
|
||
{
|
||
if (_points.Count == 2)
|
||
{
|
||
float radius = PointHelper.Distance(ScalePoint(_points[0]), ScalePoint(_points[1]));
|
||
line_path.AddEllipse(
|
||
ScalePoint(_points[0]).X - radius,
|
||
ScalePoint(_points[0]).Y - radius,
|
||
radius * 2,
|
||
radius * 2);
|
||
}
|
||
for (int i = 0; i < _points.Count; i++)
|
||
{
|
||
DrawVertex(vrtx_path, i);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case ShapeTypeEnum.LineStrip:
|
||
{
|
||
line_path.StartFigure();
|
||
line_path.AddLine(ScalePoint(_points[0]), ScalePoint(_points[0]));
|
||
|
||
for (int i = 0; i < _points.Count; i++)
|
||
{
|
||
PointF pt = _points[i];
|
||
line_path.AddLine(ScalePoint(pt), ScalePoint(pt));
|
||
DrawVertex(vrtx_path, i);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case ShapeTypeEnum.Line:
|
||
{
|
||
// 添加框线到路径
|
||
var tmpPoints = _points.Select(p => ScalePoint(p)).ToList();
|
||
line_path.AddLines(tmpPoints.ToArray());
|
||
|
||
if (IsDrawLineVirtualRect && tmpPoints.Count == 2)
|
||
{
|
||
var center = new PointF((tmpPoints[0].X + tmpPoints[1].X) / 2,
|
||
(tmpPoints[0].Y + tmpPoints[1].Y) / 2);
|
||
|
||
// 计算两点之间的角度
|
||
float dx = tmpPoints[1].X - tmpPoints[0].X;
|
||
float dy = tmpPoints[1].Y - tmpPoints[0].Y;
|
||
float distance = PointHelper.Distance(tmpPoints[0], tmpPoints[1]);
|
||
double angle = Math.Atan2(dy, dx) * (180.0 / Math.PI); // 转换为度数
|
||
|
||
float l = center.X - distance / 2;
|
||
float t = center.Y - LineVirtualRectWidth / 2;
|
||
float r = center.X + distance / 2;
|
||
float b = center.Y + LineVirtualRectWidth / 2;
|
||
|
||
PointF ptLT = new PointF(l, t);
|
||
PointF ptRT = new PointF(r, t);
|
||
PointF ptRB = new PointF(r, b);
|
||
PointF ptLB = new PointF(l, b);
|
||
#if false
|
||
RectangleF rect = new RectangleF(ptLT, new SizeF(distance, LineVirtualRectWidth));
|
||
|
||
GraphicsPath rectPath = new GraphicsPath();
|
||
rectPath.AddRectangle(rect);
|
||
|
||
//// 设置矩阵以进行旋转和位移
|
||
Matrix matrix = new Matrix();
|
||
matrix.RotateAt((float)angle, center); // 旋转
|
||
|
||
// 应用变换
|
||
rectPath.Transform(matrix);
|
||
// 画框线
|
||
painter.DrawPath(pen, rectPath);
|
||
#else
|
||
RectangleF rect = new RectangleF(ptLT, new SizeF(distance, LineVirtualRectWidth));
|
||
|
||
LineVirtualRectPoints = new PointF[4] {
|
||
ptLT,ptRT,ptRB,ptLB
|
||
};
|
||
|
||
//// 设置矩阵以进行旋转和位移
|
||
Matrix matrix = new Matrix();
|
||
matrix.RotateAt((float)angle, center); // 旋转
|
||
matrix.TransformPoints(LineVirtualRectPoints);
|
||
|
||
GraphicsPath rectPath = new GraphicsPath();
|
||
rectPath.AddPolygon(LineVirtualRectPoints);
|
||
|
||
Pen rectpen = new Pen(Color.FromArgb(60, 0, 255, 0), 1);
|
||
|
||
// 画框线
|
||
painter.DrawPath(rectpen, rectPath);
|
||
|
||
#endif
|
||
}
|
||
|
||
|
||
// 添加节点到路径
|
||
for (int i = 0; i < _points.Count; i++)
|
||
{
|
||
DrawVertex(vrtx_path, i);
|
||
}
|
||
|
||
if (IsClosed())
|
||
{
|
||
line_path.AddLine(ScalePoint(_points[0]), ScalePoint(_points[0]));
|
||
}
|
||
}
|
||
break;
|
||
case ShapeTypeEnum.Polygon:
|
||
case ShapeTypeEnum.Point:
|
||
default:
|
||
{
|
||
// 添加多边形框线到路径
|
||
line_path.AddLines(_points.Select(p => ScalePoint(p)).ToArray());
|
||
|
||
// 添加节点到路径
|
||
for (int i = 0; i < _points.Count; i++)
|
||
{
|
||
DrawVertex(vrtx_path, i);
|
||
}
|
||
|
||
if (IsClosed())
|
||
{
|
||
line_path.AddLine(ScalePoint(_points[0]), ScalePoint(_points[0]));
|
||
}
|
||
}
|
||
break;
|
||
|
||
}
|
||
|
||
|
||
#region 将点绘制到画布上
|
||
|
||
// 画框线
|
||
painter.DrawPath(pen, line_path);
|
||
|
||
// 填充节点
|
||
if (vrtx_path.PointCount > 0)
|
||
{
|
||
painter.DrawPath(pen, vrtx_path);
|
||
painter.FillPath(new SolidBrush(vertex_fill_color), vrtx_path);
|
||
}
|
||
|
||
|
||
if (fill) // 是否填充多边形
|
||
{
|
||
Color fillColor = Selected ? select_fill_color : fill_color;
|
||
painter.FillPath(new SolidBrush(fillColor), line_path);
|
||
}
|
||
|
||
#endregion
|
||
|
||
}
|
||
|
||
|
||
private Region[] _regionVertex = new Region[] { };
|
||
|
||
private void DrawVertex1(GraphicsPath path, int i)
|
||
{
|
||
PointF pt = _points[i];
|
||
_regionVertex[i] = new Region(new RectangleF(
|
||
pt.X - _epsilon, pt.Y - _epsilon,
|
||
_epsilon * 2, _epsilon * 2));
|
||
|
||
// 将节点变换
|
||
PointF[] transformedPoint = new PointF[] { pt };
|
||
_matrix.TransformPoints(transformedPoint); // 变换节点位置
|
||
|
||
pt = transformedPoint[0]; // 获取变换后的节点位置
|
||
|
||
// 绘制节点
|
||
float d = point_size; // Point size
|
||
PointTypeEnum shape = point_type; // Point shape
|
||
PointF point = ScalePoint(pt);
|
||
|
||
if (i == _highlightIndex)
|
||
{
|
||
var setting = _highlightSettings[_highlightMode];
|
||
var size = setting.PointSize;
|
||
shape = setting.PointType;
|
||
d *= size; // Example for highlighting
|
||
}
|
||
|
||
if (_highlightIndex >= 0)
|
||
{
|
||
_vertex_fill_color = hvertex_fill_color;
|
||
}
|
||
else
|
||
{
|
||
_vertex_fill_color = vertex_fill_color;
|
||
}
|
||
|
||
switch (shape)
|
||
{
|
||
case PointTypeEnum.SQUARE:
|
||
path.AddRectangle(new RectangleF(point.X - d / 2, point.Y - d / 2, d, d));
|
||
break;
|
||
case PointTypeEnum.ROUND:
|
||
path.AddEllipse(point.X - d / 2, point.Y - d / 2, d, d);
|
||
break;
|
||
default:
|
||
throw new InvalidOperationException("Unsupported vertex shape");
|
||
}
|
||
}
|
||
private void DrawVertex(GraphicsPath path, int i)
|
||
{
|
||
PointF pt = _points[i];
|
||
|
||
float d = point_size; // Point size
|
||
PointTypeEnum shape = point_type; // Point shape
|
||
PointF point = ScalePoint(pt);
|
||
|
||
if (i == _highlightIndex)
|
||
{
|
||
var setting = _highlightSettings[_highlightMode];
|
||
var size = setting.PointSize;
|
||
shape = setting.PointType;
|
||
d *= size; // Example for highlighting
|
||
}
|
||
|
||
if (_highlightIndex >= 0)
|
||
{
|
||
_vertex_fill_color = hvertex_fill_color;
|
||
}
|
||
else
|
||
{
|
||
_vertex_fill_color = vertex_fill_color;
|
||
}
|
||
|
||
switch (shape)
|
||
{
|
||
case PointTypeEnum.SQUARE:
|
||
path.AddRectangle(new RectangleF(point.X - d / 2, point.Y - d / 2, d, d));
|
||
break;
|
||
case PointTypeEnum.ROUND:
|
||
path.AddEllipse(point.X - d / 2, point.Y - d / 2, d, d);
|
||
break;
|
||
default:
|
||
throw new InvalidOperationException("Unsupported vertex shape");
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 查找离鼠标最近且距离小于阈值的节点
|
||
/// </summary>
|
||
/// <param name="point">鼠标位置</param>
|
||
/// <param name="epsilon">阈值</param>
|
||
/// <returns>返回节点的索引</returns>
|
||
public int NearestVertex(PointF point, float epsilon = DFT_VTX_EPSILON)
|
||
{
|
||
switch (ShapeType)
|
||
{
|
||
case ShapeTypeEnum.Rectangle:
|
||
{
|
||
|
||
for (int i = 0; i < _regionVertex.Length; i++)
|
||
{
|
||
if (_regionVertex[i] == null)
|
||
{
|
||
break;
|
||
}
|
||
if (_regionVertex[i].IsVisible(point))
|
||
{
|
||
return i;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
{
|
||
_epsilon = epsilon;
|
||
float min_distance = float.MaxValue;
|
||
int min_i = -1;
|
||
|
||
PointF scaledPoint = new PointF(point.X * scale, point.Y * scale);
|
||
|
||
for (int i = 0; i < _points.Count; i++)
|
||
{
|
||
// 缩放顶点
|
||
PointF scaledVertex = new PointF(_points[i].X * scale, _points[i].Y * scale);
|
||
float dist = PointHelper.Distance(scaledVertex, scaledPoint);
|
||
|
||
// 检查距离是否在 epsilon 范围内
|
||
if (dist <= epsilon && dist < min_distance)
|
||
{
|
||
min_distance = dist;
|
||
min_i = i;
|
||
}
|
||
}
|
||
|
||
return min_i;
|
||
}
|
||
}
|
||
|
||
return -1;
|
||
|
||
}
|
||
|
||
|
||
public int NearestEdge(PointF point, float epsilon)
|
||
{
|
||
float min_distance = float.MaxValue;
|
||
int post_i = -1;
|
||
|
||
PointF scaledPoint = new PointF(point.X * scale, point.Y * scale);
|
||
|
||
|
||
|
||
for (int i = 0; i < _points.Count; i++)
|
||
{
|
||
// 计算边的两个端点
|
||
PointF start = new PointF(this[i - 1].X * scale, this[i - 1].Y * scale);
|
||
PointF end = new PointF(this[i].X * scale, this[i].Y * scale);
|
||
|
||
// 计算到线段的距离
|
||
float dist = PointHelper.DistanceToLine(scaledPoint, start, end);
|
||
|
||
// 检查距离是否在 epsilon 范围内
|
||
if (dist <= epsilon && dist < min_distance)
|
||
{
|
||
min_distance = dist;
|
||
post_i = i;
|
||
}
|
||
}
|
||
|
||
return post_i;
|
||
}
|
||
|
||
|
||
public bool ContainsPoint(PointF point)
|
||
{
|
||
return MakePath().IsVisible(point);
|
||
}
|
||
|
||
|
||
|
||
private GraphicsPath MakePath()
|
||
{
|
||
GraphicsPath path = new GraphicsPath();
|
||
|
||
if (ShapeType == ShapeTypeEnum.Rectangle)
|
||
{
|
||
if (_points.Count == 2)
|
||
{
|
||
// 创建矩形路径
|
||
RectangleF rect = new RectangleF(
|
||
Math.Min(_points[0].X, _points[1].X),
|
||
Math.Min(_points[0].Y, _points[1].Y),
|
||
Math.Abs(_points[1].X - _points[0].X),
|
||
Math.Abs(_points[1].Y - _points[0].Y));
|
||
|
||
path.AddRectangle(rect);
|
||
}
|
||
}
|
||
else if (ShapeType == ShapeTypeEnum.Circle)
|
||
{
|
||
if (_points.Count == 2)
|
||
{
|
||
// 计算半径
|
||
float radius = PointHelper.Distance(_points[0], _points[1]);
|
||
path.AddEllipse(_points[0].X - radius, _points[0].Y - radius, radius * 2, radius * 2);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 处理多边形
|
||
path.StartFigure();
|
||
path.AddLine(_points[0], _points[1]);
|
||
|
||
for (int i = 2; i < _points.Count; i++)
|
||
{
|
||
path.AddLine(_points[i - 1], _points[i]);
|
||
}
|
||
path.CloseFigure(); // 结束图形
|
||
}
|
||
|
||
return path;
|
||
}
|
||
|
||
|
||
public RectangleF BoundingRect()
|
||
{
|
||
return MakePath().GetBounds();
|
||
}
|
||
|
||
|
||
|
||
public void MoveBy(PointF offset)
|
||
{
|
||
for (int i = 0; i < _points.Count; i++)
|
||
{
|
||
_points[i] = new PointF(_points[i].X + offset.X, _points[i].Y + offset.Y);
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 移动特定顶点
|
||
/// </summary>
|
||
/// <param name="index"></param>
|
||
/// <param name="offset"></param>
|
||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||
public void MoveVertexBy(int index, PointF offset)
|
||
{
|
||
if (index >= 0 && index < _points.Count)
|
||
{
|
||
_rectSelectedVertex = index;
|
||
_rectSelectedMoveVertex = new PointF(_points[index].X, _points[index].Y);
|
||
_points[index] = new PointF(_points[index].X + offset.X, _points[index].Y + offset.Y);
|
||
}
|
||
else
|
||
{
|
||
throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range.");
|
||
}
|
||
}
|
||
|
||
|
||
public void HighlightVertex(int i, HighlightModeEnum action)
|
||
{
|
||
this._highlightIndex = i;
|
||
this._highlightMode = action;
|
||
}
|
||
|
||
|
||
|
||
public void HighlightClear()
|
||
{
|
||
_highlightIndex = -1;
|
||
}
|
||
|
||
|
||
public FlyShape Copy()
|
||
{
|
||
var jsonStr = JsonConvert.SerializeObject(this);
|
||
FlyShape copyShp = JsonConvert.DeserializeObject<FlyShape>(jsonStr);
|
||
return copyShp;
|
||
}
|
||
|
||
|
||
|
||
public int Length => _points.Count();
|
||
|
||
|
||
|
||
public PointF this[int index]
|
||
{
|
||
|
||
get
|
||
{
|
||
if (index == -1)
|
||
{
|
||
return _points[_points.Count - 1];
|
||
}
|
||
return _points[index];
|
||
}
|
||
set
|
||
{
|
||
if (index == -1)
|
||
{
|
||
_points[_points.Count - 1] = value;
|
||
}
|
||
else
|
||
{
|
||
_points[index] = value;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
private void NomalizeRotateAngle()
|
||
{
|
||
if (_currentRotateAngle >= 360)
|
||
{
|
||
_currentRotateAngle %= 360;
|
||
}
|
||
else if (_currentRotateAngle < 0)
|
||
{
|
||
_currentRotateAngle = 360 - (-_currentRotateAngle % 360);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|