如果看补全不懂可以看看源码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Media;
namespace GestureDesigner.Design;
/// <summary>
/// 角度单位(弧度或角度)
/// </summary>
public enum AngleUnit
{
/// <summary>
/// 弧度制
/// </summary>
Radians,
/// <summary>
/// 角度制
/// </summary>
Degrees
}
/// <summary>
/// 水平方向(左或右)
/// </summary>
public enum HorizontalDirection
{
Right,
Left
}
/// <summary>
/// 垂直方向(上或下)
/// </summary>
public enum VerticalDirection
{
Up,
Down
}
/// <summary>
/// 手势绘制的配置选项
/// </summary>
public record Options
{
/// <summary>默认配置(角度制,向左,向上)</summary>
public static readonly Options Default = new();
/// <summary>Windows 坐标系配置(向下为正方向)</summary>
public static readonly Options Windows = new()
{
VerticalDirection = VerticalDirection.Down
};
/// <summary>弧度制配置</summary>
public static readonly Options Radians = new()
{
AngleUnit = AngleUnit.Radians
};
/// <summary>角度单位(默认角度制)</summary>
public AngleUnit AngleUnit { get; set; } = AngleUnit.Degrees;
/// <summary>X轴正方向(默认向左)</summary>
public HorizontalDirection HorizontalDirection { get; set; } = HorizontalDirection.Left;
/// <summary>Y轴正方向(默认向上)</summary>
public VerticalDirection VerticalDirection { get; set; } = VerticalDirection.Up;
/// <summary>计算精度(小数位数,默认3位)</summary>
public int Precision { get; set; } = 3;
}
/// <summary>
/// 手势绘制器,用于生成基于数学坐标的几何路径(支持直线、圆弧等)
/// </summary>
public class GestureDrawer
{
private readonly List<MathPoint> _internalPath = [];
private readonly Options _options;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="options">绘制配置(默认使用 <see cref="Options.Default"/>)</param>
public GestureDrawer(Options? options = null)
{
_options = options ?? Options.Default;
CurrentInternalLocation = new MathPoint(0, 0);
_internalPath.Add(CurrentInternalLocation);
}
private MathPoint CurrentInternalLocation { get; set; }
private float CurrentInternalDirection { get; set; }
internal IReadOnlyList<Point> DrawingPath => ConvertToPath(_internalPath);
/// <summary>
/// 设置当前方向角度(根据配置自动转换单位)
/// </summary>
/// <param name="angle">角度或弧度(取决于 <see cref="Options.AngleUnit"/>)</param>
public GestureDrawer SetDirection(float angle)
{
CurrentInternalDirection = ToInternalAngle(angle);
return this;
}
/// <summary>
/// 向前绘制指定长度的直线(沿当前方向)
/// </summary>
/// <param name="length">长度</param>
public GestureDrawer Forward(float length)
{
var angleInRadians = CurrentInternalDirection * (Math.PI / 180.0);
var endPoint = new MathPoint(
CurrentInternalLocation.X + length * Math.Round(Math.Cos(angleInRadians), _options.Precision),
CurrentInternalLocation.Y + length * Math.Round(Math.Sin(angleInRadians), _options.Precision)
);
DrawLine(CurrentInternalLocation, endPoint);
CurrentInternalLocation = endPoint;
return this;
}
/// <summary>
/// 绘制指定长度和角度的直线
/// </summary>
/// <param name="length">长度</param>
/// <param name="angle">角度或弧度</param>
public GestureDrawer DrawLine(float length, float angle)
{
SetDirection(angle);
return Forward(length);
}
/// <summary>
/// 旋转当前方向(叠加角度)
/// </summary>
/// <param name="angle">旋转角度或弧度</param>
public GestureDrawer Rotate(float angle)
{
SetDirection(CurrentInternalDirection + angle);
return this;
}
/// <summary>
/// 绘制圆弧(相对于当前坐标)
/// </summary>
/// <param name="relativeX">圆心X偏移</param>
/// <param name="relativeY">圆心Y偏移</param>
/// <param name="radius">半径</param>
/// <param name="startAngle">起始角度</param>
/// <param name="sweepAngle">扫过角度(可为负,表示反方向旋转)</param>
public GestureDrawer DrawArc(float relativeX, float relativeY, float radius, float startAngle,
float sweepAngle)
{
return Arc((float)CurrentInternalLocation.X + relativeX,
(float)CurrentInternalLocation.Y + relativeY,
radius, startAngle, sweepAngle);
}
/// <summary>
/// 绘制圆弧(绝对圆心坐标)
/// </summary>
/// <param name="centerX">圆心X坐标</param>
/// <param name="centerY">圆心Y坐标</param>
/// <param name="radius">半径</param>
/// <param name="startAngle">起始角度</param>
/// <param name="sweepAngle">扫过角度(可为负,表示反方向旋转)</param>
public GestureDrawer Arc(float centerX, float centerY, float radius, float startAngle,
float sweepAngle)
{
var internalCenter = new MathPoint(centerX, centerY);
var internalStartAngle = ToInternalAngle(startAngle);
var sweepDegrees = ToDegrees(sweepAngle);
var internalEndAngle = internalStartAngle + sweepDegrees;
var steps = Math.Max(10, (int)Math.Abs(sweepDegrees));
for (var i = 0; i <= steps; i++)
{
var currentInternalAngle = internalStartAngle + i * (sweepDegrees / steps);
var angleRad = currentInternalAngle * (Math.PI / 180.0);
var pointOnArc = new MathPoint(
internalCenter.X + radius * Math.Round(Math.Cos(angleRad), _options.Precision),
internalCenter.Y + radius * Math.Round(Math.Sin(angleRad), _options.Precision)
);
_internalPath.Add(pointOnArc);
}
var endAngleRad = internalEndAngle * (Math.PI / 180.0);
CurrentInternalLocation = new MathPoint(
internalCenter.X + radius * Math.Round(Math.Cos(endAngleRad), _options.Precision),
internalCenter.Y + radius * Math.Round(Math.Sin(endAngleRad), _options.Precision)
);
CurrentInternalDirection = (internalEndAngle + (sweepDegrees >= 0 ? 90 : -90) + 360) % 360;
return this;
}
internal PathGeometry ToPathGeometry()
{
var path = DrawingPath;
if (path.Count == 0)
return new PathGeometry();
var pathGeometry = new PathGeometry();
var pathFigure = new PathFigure { StartPoint = path[0] };
for (var i = 1; i < path.Count; i++)
pathFigure.Segments.Add(new LineSegment(path[i], true));
pathGeometry.Figures.Add(pathFigure);
return pathGeometry;
}
private float ToDegrees(float angleInUnit)
{
//角度制填角度,弧度制填pi的系数
return _options.AngleUnit == AngleUnit.Radians
? (float)(angleInUnit * 180.0)
: angleInUnit;
}
private float ToInternalAngle(float angle)
{
var degrees = ToDegrees(angle); //转角度制
Debug.WriteLine(degrees, "ToInternalAngle_degrees");
var effectiveAngle = degrees;
if (_options.HorizontalDirection == HorizontalDirection.Right) effectiveAngle += 180;
Debug.WriteLine(effectiveAngle, "ToInternalAngle_effectiveAngle");
return (effectiveAngle % 360 + 360) % 360; //去除周期,转为0~359的正角度
}
private IReadOnlyList<Point> ConvertToPath(List<MathPoint> internalPath)
{
var xSign = _options.HorizontalDirection == HorizontalDirection.Left ? 1 : -1;
var ySign = _options.VerticalDirection == VerticalDirection.Down ? 1 : -1;
return internalPath.Select(p => new Point(p.X * xSign, p.Y * ySign)).ToList().AsReadOnly();
}
private void DrawLine(MathPoint start, MathPoint end)
{
// Simple line interpolation in internal coordinate space
var distance = Math.Sqrt(Math.Pow(end.X - start.X, 2) + Math.Pow(end.Y - start.Y, 2));
const float stepSize = 1.0f;
var steps = (int)(distance / stepSize);
if (steps <= 0)
{
_internalPath.Add(end);
return;
}
for (var i = 1; i <= steps; i++)
{
var t = i / (double)steps;
var point = new MathPoint(
start.X + (end.X - start.X) * t,
start.Y + (end.Y - start.Y) * t
);
_internalPath.Add(point);
}
}
private readonly struct MathPoint(double x, double y)
{
public double X { get; } = x;
public double Y { get; } = y;
}
}
发这个配合另一个帖子的示例,理论上是可以的
回头有空我再试试吧,主要是大佬之前的画手势的动作已经十分强大了,基本想要的图形都完美画出来了