diff --git a/CanFly.Canvas/CanFly.Canvas.csproj b/CanFly.Canvas/CanFly.Canvas.csproj
new file mode 100644
index 0000000..8037f3d
--- /dev/null
+++ b/CanFly.Canvas/CanFly.Canvas.csproj
@@ -0,0 +1,28 @@
+
+
+
+
+ net8.0-windows
+ enable
+ enable
+ ..\
+ output
+ true
+ true
+ AnyCPU;x64
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CanFly.Canvas/Helper/PointHelper.cs b/CanFly.Canvas/Helper/PointHelper.cs
new file mode 100644
index 0000000..e8bb4c9
--- /dev/null
+++ b/CanFly.Canvas/Helper/PointHelper.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing.Drawing2D;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CanFly.Canvas.Helper
+{
+ public static class PointHelper
+ {
+ public static Point ToPoint(this PointF pf)
+ {
+ return new Point((int)pf.X, (int)pf.Y);
+ }
+
+ ///
+ /// 将相对于控件的坐标转换为相对于图像的坐标
+ ///
+ /// 控件中指定点的点位坐标,坐标原点为控件左上角
+ /// 该点以图像坐标系为基准的坐标值,坐标原点为图像左上角
+ public static PointF ToImageCoordinate(this Point p, Matrix m)
+ {
+ PointF pf = new PointF(p.X, p.Y);
+ return ToImageCoordinate(pf, m);
+ }
+
+ ///
+ /// 将相对于控件的坐标转换为相对于图像的坐标
+ ///
+ /// 控件中指定点的点位坐标,坐标原点为控件左上角
+ /// 该点以图像坐标系为基准的坐标值,坐标原点为图像左上角
+ public static PointF ToImageCoordinate(this PointF p, Matrix m)
+ {
+ PointF[] ps = new PointF[] { p };
+ Matrix invertMatrix = m.Clone();
+ //想要从旧空间到新空间的逆变换,所以我们需要对这个矩阵求逆
+ invertMatrix.Invert();
+ invertMatrix.TransformPoints(ps);
+ return ps[0];
+ }
+
+ ///
+ /// 将相对于图像的坐标转换为相对于控件坐标系
+ ///
+ /// 图像中指定点的点位坐标,坐标原点为图像左上角
+ /// 该点以空间坐标系为基准的坐标值,坐标原点为空间坐左上角
+ public static PointF ToControlCoordinate(this PointF p, Matrix m)
+ {
+ PointF[] ps = new PointF[] { p };
+ m.TransformPoints(ps);
+ return ps[0];
+ }
+
+
+
+
+ public static float Distance(PointF p1, PointF p2)
+ {
+ return (float)Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
+ }
+
+ public static float DistanceToLine(PointF point, PointF start, PointF end)
+ {
+ float lineLengthSquared = DistanceSquared(start, end);
+ if (lineLengthSquared == 0)
+ {
+ return Distance(point, start); // 线段的两个端点重合
+ }
+
+ float t = ((point.X - start.X) * (end.X - start.X) + (point.Y - start.Y) * (end.Y - start.Y)) / lineLengthSquared;
+ t = Math.Clamp(t, 0, 1); // 限制 t 在 [0, 1] 范围内
+
+ PointF projection = new PointF(
+ start.X + t * (end.X - start.X),
+ start.Y + t * (end.Y - start.Y));
+
+ return Distance(point, projection);
+ }
+
+
+ public static float DistanceSquared(PointF p1, PointF p2)
+ {
+ return (float)(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
+ }
+
+ }
+}
diff --git a/CanFly.Canvas/Model/ClickArea.cs b/CanFly.Canvas/Model/ClickArea.cs
new file mode 100644
index 0000000..802973c
--- /dev/null
+++ b/CanFly.Canvas/Model/ClickArea.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CanFly.Canvas.Model
+{
+ ///
+ /// 点击的区域
+ ///
+ internal enum ClickArea
+ {
+
+ ///
+ /// 未知区域
+ ///
+ AREA_UNKNOW,
+
+
+ ///
+ /// 图片区域
+ ///
+ AREA_IMG,
+
+
+ ///
+ /// 缺陷元素区域
+ ///
+ AREA_DEFECT,
+ }
+}
diff --git a/CanFly.Canvas/Model/Cursor.cs b/CanFly.Canvas/Model/Cursor.cs
new file mode 100644
index 0000000..dd838aa
--- /dev/null
+++ b/CanFly.Canvas/Model/Cursor.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LabelSharp.Config
+{
+ public static class CustomCursors
+ {
+
+ public static Cursor CURSOR_DEFAULT = Cursors.Arrow;
+ public static Cursor CURSOR_POINT = Cursors.Hand;
+ public static Cursor CURSOR_DRAW = Cursors.Cross;
+ public static Cursor CURSOR_MOVE = Cursors.Hand;
+ public static Cursor CURSOR_GRAB = Cursors.Hand;
+
+
+
+
+ }
+}
diff --git a/CanFly.Canvas/Model/Exception/InvalidShapeException.cs b/CanFly.Canvas/Model/Exception/InvalidShapeException.cs
new file mode 100644
index 0000000..6b1ba58
--- /dev/null
+++ b/CanFly.Canvas/Model/Exception/InvalidShapeException.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CanFly.Canvas.Model.Exception
+{
+ internal class InvalidShapeException : System.Exception
+ {
+ }
+}
diff --git a/CanFly.Canvas/Shape/BaseShape.cs b/CanFly.Canvas/Shape/BaseShape.cs
new file mode 100644
index 0000000..b5acaa8
--- /dev/null
+++ b/CanFly.Canvas/Shape/BaseShape.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CanFly.Canvas.Shape
+{
+ public abstract class BaseShape
+ {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+}
diff --git a/CanFly.Canvas/Shape/DoubleClickActionEnum.cs b/CanFly.Canvas/Shape/DoubleClickActionEnum.cs
new file mode 100644
index 0000000..944c28d
--- /dev/null
+++ b/CanFly.Canvas/Shape/DoubleClickActionEnum.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CanFly.Canvas.Shape
+{
+ internal enum DoubleClickActionEnum
+ {
+ None,
+ Close,
+
+ }
+}
diff --git a/CanFly.Canvas/Shape/FlyShape.cs b/CanFly.Canvas/Shape/FlyShape.cs
new file mode 100644
index 0000000..4d96583
--- /dev/null
+++ b/CanFly.Canvas/Shape/FlyShape.cs
@@ -0,0 +1,938 @@
+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 _points
+ {
+ get;
+ set;
+ } = new List();
+
+ public List Points
+ {
+ get { return _points; }
+ set
+ {
+ this._points = value;
+ }
+ }
+
+
+ private List _pointsRaw = new List();
+
+
+ ///
+ /// 辅助节点
+ ///
+ public List GuidePoints = new List();
+
+ public float _currentRotateAngle;
+ private bool _isRotating = false;
+
+
+ public List point_labels = new List();
+
+
+ private ShapeTypeEnum _shape_type_raw;
+
+
+ ///
+ /// 是否填充多边形。使用:select_fill_color 或 fill_color 填充。
+ ///
+ public bool fill = false;
+
+
+ public bool Selected { get; set; } = false;
+ public object? flags;
+ public string description = "";
+ private List