fix:issue #120
This commit is contained in:
@@ -17,6 +17,41 @@ namespace Ink_Canvas {
|
|||||||
private List<Circle> circles = new List<Circle>();
|
private List<Circle> circles = new List<Circle>();
|
||||||
private const double LINE_STRAIGHTEN_THRESHOLD = 0.20; // 默认灵敏度阈值,与UI默认值对应
|
private const double LINE_STRAIGHTEN_THRESHOLD = 0.20; // 默认灵敏度阈值,与UI默认值对应
|
||||||
|
|
||||||
|
// 矩形参考线系统
|
||||||
|
private List<RectangleGuideLine> rectangleGuideLines = new List<RectangleGuideLine>();
|
||||||
|
private const double RECTANGLE_ENDPOINT_THRESHOLD = 30.0; // 端点相交判断阈值
|
||||||
|
private const double RECTANGLE_ANGLE_THRESHOLD = 15.0; // 角度判断阈值(度)
|
||||||
|
|
||||||
|
// 矩形参考线数据结构
|
||||||
|
private class RectangleGuideLine
|
||||||
|
{
|
||||||
|
public Stroke OriginalStroke { get; set; }
|
||||||
|
public Point StartPoint { get; set; }
|
||||||
|
public Point EndPoint { get; set; }
|
||||||
|
public DateTime CreatedTime { get; set; }
|
||||||
|
public double Angle { get; set; } // 直线角度(弧度)
|
||||||
|
public bool IsHorizontal { get; set; }
|
||||||
|
public bool IsVertical { get; set; }
|
||||||
|
|
||||||
|
public RectangleGuideLine(Stroke stroke, Point start, Point end)
|
||||||
|
{
|
||||||
|
OriginalStroke = stroke;
|
||||||
|
StartPoint = start;
|
||||||
|
EndPoint = end;
|
||||||
|
CreatedTime = DateTime.Now;
|
||||||
|
|
||||||
|
// 计算角度
|
||||||
|
double deltaX = end.X - start.X;
|
||||||
|
double deltaY = end.Y - start.Y;
|
||||||
|
Angle = Math.Atan2(deltaY, deltaX);
|
||||||
|
|
||||||
|
// 判断是否为水平或垂直线
|
||||||
|
double angleDegrees = Math.Abs(Angle * 180.0 / Math.PI);
|
||||||
|
IsHorizontal = angleDegrees < RECTANGLE_ANGLE_THRESHOLD || angleDegrees > (180 - RECTANGLE_ANGLE_THRESHOLD);
|
||||||
|
IsVertical = Math.Abs(angleDegrees - 90) < RECTANGLE_ANGLE_THRESHOLD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void inkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e) {
|
private void inkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e) {
|
||||||
// 标记是否进行了直线拉直
|
// 标记是否进行了直线拉直
|
||||||
bool wasStraightened = false;
|
bool wasStraightened = false;
|
||||||
@@ -194,6 +229,9 @@ namespace Ink_Canvas {
|
|||||||
if (!inkCanvas.Strokes.Contains(circles[i].Stroke))
|
if (!inkCanvas.Strokes.Contains(circles[i].Stroke))
|
||||||
circles.RemoveAt(i);
|
circles.RemoveAt(i);
|
||||||
|
|
||||||
|
// 处理矩形参考线系统
|
||||||
|
ProcessRectangleGuideLines(e.Stroke);
|
||||||
|
|
||||||
var strokeReco = new StrokeCollection();
|
var strokeReco = new StrokeCollection();
|
||||||
var result = InkRecognizeHelper.RecognizeShape(newStrokes);
|
var result = InkRecognizeHelper.RecognizeShape(newStrokes);
|
||||||
for (var i = newStrokes.Count - 1; i >= 0; i--) {
|
for (var i = newStrokes.Count - 1; i >= 0; i--) {
|
||||||
@@ -1624,5 +1662,285 @@ namespace Ink_Canvas {
|
|||||||
double scaleH = screenHeight / baseHeight;
|
double scaleH = screenHeight / baseHeight;
|
||||||
return (scaleW + scaleH) / 2.0;
|
return (scaleW + scaleH) / 2.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region 矩形参考线系统
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理矩形参考线系统
|
||||||
|
/// </summary>
|
||||||
|
private void ProcessRectangleGuideLines(Stroke newStroke)
|
||||||
|
{
|
||||||
|
// 只有启用矩形识别时才处理
|
||||||
|
if (!Settings.InkToShape.IsInkToShapeRectangle) return;
|
||||||
|
|
||||||
|
// 检查新笔画是否为直线
|
||||||
|
if (!IsPotentialStraightLine(newStroke)) return;
|
||||||
|
|
||||||
|
Point startPoint = newStroke.StylusPoints[0].ToPoint();
|
||||||
|
Point endPoint = newStroke.StylusPoints[newStroke.StylusPoints.Count - 1].ToPoint();
|
||||||
|
|
||||||
|
// 创建新的参考线
|
||||||
|
var newGuideLine = new RectangleGuideLine(newStroke, startPoint, endPoint);
|
||||||
|
|
||||||
|
// 清理过期的参考线(超过30秒的)
|
||||||
|
CleanupExpiredGuideLines();
|
||||||
|
|
||||||
|
// 添加新参考线
|
||||||
|
rectangleGuideLines.Add(newGuideLine);
|
||||||
|
|
||||||
|
// 检查是否可以构成矩形
|
||||||
|
CheckForRectangleFormation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清理过期的参考线
|
||||||
|
/// </summary>
|
||||||
|
private void CleanupExpiredGuideLines()
|
||||||
|
{
|
||||||
|
var expireTime = DateTime.Now.AddSeconds(-30); // 30秒过期
|
||||||
|
for (int i = rectangleGuideLines.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var guideLine = rectangleGuideLines[i];
|
||||||
|
if (guideLine.CreatedTime < expireTime || !inkCanvas.Strokes.Contains(guideLine.OriginalStroke))
|
||||||
|
{
|
||||||
|
rectangleGuideLines.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查是否可以构成矩形
|
||||||
|
/// </summary>
|
||||||
|
private void CheckForRectangleFormation()
|
||||||
|
{
|
||||||
|
if (rectangleGuideLines.Count < 4) return;
|
||||||
|
|
||||||
|
// 尝试找到四条能构成矩形的直线
|
||||||
|
var rectangleLines = FindRectangleLines();
|
||||||
|
if (rectangleLines != null && rectangleLines.Count == 4)
|
||||||
|
{
|
||||||
|
// 创建矩形并替换原有直线
|
||||||
|
CreateRectangleFromLines(rectangleLines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 寻找能构成矩形的四条直线
|
||||||
|
/// </summary>
|
||||||
|
private List<RectangleGuideLine> FindRectangleLines()
|
||||||
|
{
|
||||||
|
// 按时间排序,优先考虑最近绘制的直线
|
||||||
|
var sortedLines = rectangleGuideLines.OrderByDescending(l => l.CreatedTime).ToList();
|
||||||
|
|
||||||
|
// 尝试不同的四条直线组合
|
||||||
|
for (int i = 0; i < sortedLines.Count - 3; i++)
|
||||||
|
{
|
||||||
|
for (int j = i + 1; j < sortedLines.Count - 2; j++)
|
||||||
|
{
|
||||||
|
for (int k = j + 1; k < sortedLines.Count - 1; k++)
|
||||||
|
{
|
||||||
|
for (int l = k + 1; l < sortedLines.Count; l++)
|
||||||
|
{
|
||||||
|
var lines = new List<RectangleGuideLine> { sortedLines[i], sortedLines[j], sortedLines[k], sortedLines[l] };
|
||||||
|
if (CanFormRectangle(lines))
|
||||||
|
{
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断四条直线是否能构成矩形
|
||||||
|
/// </summary>
|
||||||
|
private bool CanFormRectangle(List<RectangleGuideLine> lines)
|
||||||
|
{
|
||||||
|
if (lines.Count != 4) return false;
|
||||||
|
|
||||||
|
// 分类水平线和垂直线
|
||||||
|
var horizontalLines = lines.Where(l => l.IsHorizontal).ToList();
|
||||||
|
var verticalLines = lines.Where(l => l.IsVertical).ToList();
|
||||||
|
|
||||||
|
// 必须有2条水平线和2条垂直线
|
||||||
|
if (horizontalLines.Count != 2 || verticalLines.Count != 2) return false;
|
||||||
|
|
||||||
|
// 检查端点相交关系
|
||||||
|
return CheckEndpointConnections(horizontalLines, verticalLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查端点相交关系
|
||||||
|
/// </summary>
|
||||||
|
private bool CheckEndpointConnections(List<RectangleGuideLine> horizontalLines, List<RectangleGuideLine> verticalLines)
|
||||||
|
{
|
||||||
|
// 收集所有端点
|
||||||
|
var allEndpoints = new List<Point>();
|
||||||
|
foreach (var line in horizontalLines.Concat(verticalLines))
|
||||||
|
{
|
||||||
|
allEndpoints.Add(line.StartPoint);
|
||||||
|
allEndpoints.Add(line.EndPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否有4个相交点(允许一定误差)
|
||||||
|
var intersectionPoints = new List<Point>();
|
||||||
|
|
||||||
|
foreach (var hLine in horizontalLines)
|
||||||
|
{
|
||||||
|
foreach (var vLine in verticalLines)
|
||||||
|
{
|
||||||
|
var intersection = GetLineIntersection(hLine, vLine);
|
||||||
|
if (intersection.HasValue)
|
||||||
|
{
|
||||||
|
// 检查交点是否在两条线段的端点附近
|
||||||
|
if (IsPointNearLineEndpoints(intersection.Value, hLine) &&
|
||||||
|
IsPointNearLineEndpoints(intersection.Value, vLine))
|
||||||
|
{
|
||||||
|
intersectionPoints.Add(intersection.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 需要有4个交点才能构成矩形
|
||||||
|
return intersectionPoints.Count >= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算两条直线的交点
|
||||||
|
/// </summary>
|
||||||
|
private Point? GetLineIntersection(RectangleGuideLine line1, RectangleGuideLine line2)
|
||||||
|
{
|
||||||
|
double x1 = line1.StartPoint.X, y1 = line1.StartPoint.Y;
|
||||||
|
double x2 = line1.EndPoint.X, y2 = line1.EndPoint.Y;
|
||||||
|
double x3 = line2.StartPoint.X, y3 = line2.StartPoint.Y;
|
||||||
|
double x4 = line2.EndPoint.X, y4 = line2.EndPoint.Y;
|
||||||
|
|
||||||
|
double denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||||
|
if (Math.Abs(denom) < 1e-10) return null; // 平行线
|
||||||
|
|
||||||
|
double t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom;
|
||||||
|
double u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom;
|
||||||
|
|
||||||
|
double intersectionX = x1 + t * (x2 - x1);
|
||||||
|
double intersectionY = y1 + t * (y2 - y1);
|
||||||
|
|
||||||
|
return new Point(intersectionX, intersectionY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查点是否在直线端点附近
|
||||||
|
/// </summary>
|
||||||
|
private bool IsPointNearLineEndpoints(Point point, RectangleGuideLine line)
|
||||||
|
{
|
||||||
|
double distToStart = GetDistance(point, line.StartPoint);
|
||||||
|
double distToEnd = GetDistance(point, line.EndPoint);
|
||||||
|
|
||||||
|
return distToStart <= RECTANGLE_ENDPOINT_THRESHOLD || distToEnd <= RECTANGLE_ENDPOINT_THRESHOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从四条直线创建矩形
|
||||||
|
/// </summary>
|
||||||
|
private void CreateRectangleFromLines(List<RectangleGuideLine> lines)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 计算矩形的四个角点
|
||||||
|
var corners = CalculateRectangleCorners(lines);
|
||||||
|
if (corners == null || corners.Count != 4) return;
|
||||||
|
|
||||||
|
// 创建矩形笔画
|
||||||
|
var pointList = new List<Point>(corners) { corners[0] }; // 闭合矩形
|
||||||
|
var point = new StylusPointCollection(pointList);
|
||||||
|
var rectangleStroke = new Stroke(GenerateFakePressureRectangle(point))
|
||||||
|
{
|
||||||
|
DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 移除原有的四条直线
|
||||||
|
SetNewBackupOfStroke();
|
||||||
|
_currentCommitType = CommitReason.ShapeRecognition;
|
||||||
|
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
if (inkCanvas.Strokes.Contains(line.OriginalStroke))
|
||||||
|
{
|
||||||
|
inkCanvas.Strokes.Remove(line.OriginalStroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新的矩形
|
||||||
|
inkCanvas.Strokes.Add(rectangleStroke);
|
||||||
|
_currentCommitType = CommitReason.UserInput;
|
||||||
|
|
||||||
|
// 清理参考线
|
||||||
|
foreach (var line in lines)
|
||||||
|
{
|
||||||
|
rectangleGuideLines.Remove(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空新笔画集合,避免重复处理
|
||||||
|
newStrokes = new StrokeCollection();
|
||||||
|
|
||||||
|
Debug.WriteLine("成功创建矩形参考线矩形");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"创建矩形时出错: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算矩形的四个角点
|
||||||
|
/// </summary>
|
||||||
|
private List<Point> CalculateRectangleCorners(List<RectangleGuideLine> lines)
|
||||||
|
{
|
||||||
|
var horizontalLines = lines.Where(l => l.IsHorizontal).ToList();
|
||||||
|
var verticalLines = lines.Where(l => l.IsVertical).ToList();
|
||||||
|
|
||||||
|
if (horizontalLines.Count != 2 || verticalLines.Count != 2) return null;
|
||||||
|
|
||||||
|
var corners = new List<Point>();
|
||||||
|
|
||||||
|
// 计算四个交点
|
||||||
|
foreach (var hLine in horizontalLines)
|
||||||
|
{
|
||||||
|
foreach (var vLine in verticalLines)
|
||||||
|
{
|
||||||
|
var intersection = GetLineIntersection(hLine, vLine);
|
||||||
|
if (intersection.HasValue)
|
||||||
|
{
|
||||||
|
corners.Add(intersection.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (corners.Count != 4) return null;
|
||||||
|
|
||||||
|
// 按顺序排列角点(顺时针或逆时针)
|
||||||
|
return SortRectangleCorners(corners);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按顺序排列矩形角点
|
||||||
|
/// </summary>
|
||||||
|
private List<Point> SortRectangleCorners(List<Point> corners)
|
||||||
|
{
|
||||||
|
if (corners.Count != 4) return corners;
|
||||||
|
|
||||||
|
// 计算中心点
|
||||||
|
double centerX = corners.Average(p => p.X);
|
||||||
|
double centerY = corners.Average(p => p.Y);
|
||||||
|
var center = new Point(centerX, centerY);
|
||||||
|
|
||||||
|
// 按角度排序
|
||||||
|
return corners.OrderBy(p => Math.Atan2(p.Y - center.Y, p.X - center.X)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user