using Ink_Canvas.Helpers;
using Ink_Canvas.Ink.Native;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using Point = System.Windows.Point;

namespace Ink_Canvas
{
    /// <summary>
    /// 主窗口类的部分类，包含压感模拟和墨水到形状识别的功能
    /// </summary>
    /// <remarks>
    /// 本文件主要包含以下功能：
    /// 1. 压感模拟：根据输入设备类型和设置模拟不同的压感效果
    /// 2. 墨水到形状识别：将手绘墨迹转换为规则形状（直线、圆形、椭圆、三角形、矩形等）
    /// 3. 直线自动拉直：将近似直线的墨迹自动拉成直线
    /// 4. 端点吸附：将直线端点吸附到其他直线的端点
    /// 5. 矩形参考线系统：通过多条直线构成矩形
    /// 6. 高级贝塞尔曲线平滑：对墨迹进行平滑处理
    /// 7. 异步墨水处理：提高性能的异步墨水处理机制
    /// </remarks>
    public partial class MainWindow : Ink_Canvas.Helpers.PerformanceTransparentWin
    {
        private Helpers.ModernInkAnalyzer _modernInkAnalyzer;
        private Helpers.ModernInkAnalyzer ModernInkAnalyzer =>
            _modernInkAnalyzer ??= new Helpers.ModernInkAnalyzer();

        /// <summary>
        /// 存储新的笔画集合，用于形状识别
        /// </summary>
        private StrokeCollection newStrokes = new StrokeCollection();

        /// <summary>
        /// 存储圆形形状的列表
        /// </summary>
        private List<Circle> circles = new List<Circle>();

        /// <summary>串行执行墨迹转形状。WinRT 必须在 Dispatcher 上延后执行：若在 StrokeCollected（UI 线程）内对 WinRT 路径同步 GetResult()，AnalyzeAsync 延续再贴回 UI 时会死锁。</summary>
        private static readonly SemaphoreSlim InkToShapeSerial = new SemaphoreSlim(1, 1);

        /// <summary>
        /// 收笔后待参与「手写体字形替换」的最近笔迹（多笔一字/一词时由 WinRT InkAnalyzer 合并为 InkWord）。
        /// </summary>
        private readonly StrokeCollection _handwritingRecentStrokesForBeautify = new StrokeCollection();

        private const int HandwritingBeautifyMaxRecentStrokes = 20;

        /// <summary>手写体矫正：停笔后延迟执行（毫秒），多笔一字时等用户写完再识别。</summary>
        private const int HandwritingBeautifyDebounceMs = 1000;

        /// <summary>
        /// 运行时停笔防抖值：取 settings（300-5000），缺失或越界时回退到 <see cref="HandwritingBeautifyDebounceMs"/>。
        /// 每次并入批次时由 <see cref="ScheduleHandwritingGlyphReplaceAfterStrokeCollected"/> 读取，
        /// 因此改设置后下一批立即生效，无需失效通知。
        /// </summary>
        private static int CurrentHandwritingBeautifyDebounceMs
        {
            get
            {
                var configured = MainWindow.Settings?.InkToShape?.HandwritingBeautifyDebounceMs ?? 0;
                if (configured < 300 || configured > 5000)
                    return HandwritingBeautifyDebounceMs;
                return configured;
            }
        }

        private DispatcherTimer _handwritingBeautifyDebounceTimer;

        /// <summary>每次收笔并入批次时递增；防抖 Tick 携带快照，识别过程中若又有新笔则放弃本轮替换。</summary>
        private ulong _handwritingBeautifyScheduleRevision;

        /// <summary>画布笔画 → 手写纠正的独立识别快照；快照使用最终画布几何，不与可变画布笔画共享点集。</summary>
        private readonly Dictionary<Stroke, Stroke> _handwritingBeautifyInkInputByCanvasStroke =
            new Dictionary<Stroke, Stroke>();

        /// <summary>
        /// 矩形参考线的列表
        /// </summary>
        private List<RectangleGuideLine> rectangleGuideLines = new List<RectangleGuideLine>();

        /// <summary>
        /// 矩形端点的阈值
        /// </summary>
        private const double RECTANGLE_ENDPOINT_THRESHOLD = 30.0;

        /// <summary>
        /// 矩形角度的阈值
        /// </summary>
        private const double RECTANGLE_ANGLE_THRESHOLD = 15.0;

        /// <summary>
        /// 矩形参考线类，用于辅助矩形绘制
        /// </summary>
        private class RectangleGuideLine
        {
            /// <summary>
            /// 原始笔画
            /// </summary>
            public Stroke OriginalStroke { get; set; }

            /// <summary>
            /// 起始点
            /// </summary>
            public Point StartPoint { get; set; }

            /// <summary>
            /// 结束点
            /// </summary>
            public Point EndPoint { get; set; }

            /// <summary>
            /// 创建时间
            /// </summary>
            public DateTime CreatedTime { get; set; }

            /// <summary>
            /// 角度
            /// </summary>
            public double Angle { get; set; }

            /// <summary>
            /// 是否为水平线
            /// </summary>
            public bool IsHorizontal { get; set; }

            /// <summary>
            /// 是否为垂直线
            /// </summary>
            public bool IsVertical { get; set; }

            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="stroke">原始笔画</param>
            /// <param name="start">起始点</param>
            /// <param name="end">结束点</param>
            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;
            }
        }

        /// <summary>
        /// 收笔后压感/墨迹平滑等尾部处理。返回「当前应登记到手写字形替换批次」的画布笔画引用：
        /// 同步贝塞尔平滑若替换了笔画，则为新 <see cref="Stroke"/>；否则为 <paramref name="e"/>.Stroke。
        /// 直线拉直后事件参数中的笔画可能已不在画布上，调用方需另行传入画布上的笔画（见收笔处）。
        /// </summary>
        private Stroke RunStrokeCollectedPostShapeRecognitionTail(InkCanvasStrokeCollectedEventArgs e, bool wasStraightened)
        {
            if (e?.Stroke == null)
                return null;

            var handwritingScheduleStroke = e.Stroke;

            try
            {
                // 原生湿墨提交的笔画压感是湿墨预览最终值（必含非 0.5 点），压感守卫不能挡它，
                // 否则新墨迹系统的干墨永远进不了墨迹平滑（性能面板平滑计时全 0）。
                // 平滑器沿几何方向线性插值压感、不重写压感值；InkStyle 0/1/3 重写也已在
                // 下方对 isNativeWetInkCommitted 跳过，放行不会造成二次烘焙或烘干闪变。
                if (!e.Stroke.ContainsPropertyData(NativeWetInkCommittedGuid))
                {
                    foreach (var stylusPoint in e.Stroke.StylusPoints)
                        if ((stylusPoint.PressureFactor > 0.501 || stylusPoint.PressureFactor < 0.5) &&
                            stylusPoint.PressureFactor != 0)
                            return e.Stroke;
                }

                try
                {
                    if (e.Stroke.StylusPoints.Count > 3)
                    {
                        var random = new Random();
                        var _speed = GetPointSpeed(
                            e.Stroke.StylusPoints[random.Next(0, e.Stroke.StylusPoints.Count - 1)].ToPoint(),
                            e.Stroke.StylusPoints[random.Next(0, e.Stroke.StylusPoints.Count - 1)].ToPoint(),
                            e.Stroke.StylusPoints[random.Next(0, e.Stroke.StylusPoints.Count - 1)].ToPoint());

                        RandWindow.randSeed = (int)(_speed * 100000 * 1000);
                    }
                }
                catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }

                // 原生湿墨提交：湿预览压感已是最终值。此处再跑 InkStyle 0/1 会重写
                // PressureFactor（尤其是 InkStyle 0 的收笔变细），抬笔瞬间干墨变样 → 烘干闪变。
                // 「屏蔽压感」已在收笔主路径将点集归一成 0.5；此处若再跑 InkStyle 0/1 同样造成假压感。
                var isNativeWetInkCommitted =
                    e.Stroke.ContainsPropertyData(NativeWetInkCommittedGuid);
                if (!Settings.Canvas.DisablePressure && !isNativeWetInkCommitted)
                {
                    switch (Settings.Canvas.InkStyle)
                    {
                        case 1:
                            if (penType == 0)
                                try
                                {
                                    var stylusPoints = new StylusPointCollection();
                                    var n = e.Stroke.StylusPoints.Count - 1;

                                    for (var i = 0; i <= n; i++)
                                    {
                                        var speed = GetPointSpeed(e.Stroke.StylusPoints[Math.Max(i - 1, 0)].ToPoint(),
                                            e.Stroke.StylusPoints[i].ToPoint(),
                                            e.Stroke.StylusPoints[Math.Min(i + 1, n)].ToPoint());
                                        var point = new StylusPoint
                                        {
                                            PressureFactor = RateBasedPressureFactorFromPointSpeed(speed),
                                            X = e.Stroke.StylusPoints[i].X,
                                            Y = e.Stroke.StylusPoints[i].Y
                                        };
                                        stylusPoints.Add(point);
                                    }

                                    e.Stroke.StylusPoints = stylusPoints;
                                }
                                catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }

                            break;
                        case 0:
                            if (penType == 0)
                                try
                                {
                                    var stylusPoints = new StylusPointCollection();
                                    var n = e.Stroke.StylusPoints.Count - 1;
                                    var pressure = 0.1;
                                    var x = 10;
                                    if (n == 1) return e.Stroke;
                                    if (n >= x)
                                    {
                                        for (var i = 0; i < n - x; i++)
                                        {
                                            var point = new StylusPoint();

                                            point.PressureFactor = (float)0.5;
                                            point.X = e.Stroke.StylusPoints[i].X;
                                            point.Y = e.Stroke.StylusPoints[i].Y;
                                            stylusPoints.Add(point);
                                        }

                                        for (var i = n - x; i <= n; i++)
                                        {
                                            var point = new StylusPoint();

                                            point.PressureFactor = (float)((0.5 - pressure) * (n - i) / x + pressure);
                                            point.X = e.Stroke.StylusPoints[i].X;
                                            point.Y = e.Stroke.StylusPoints[i].Y;
                                            stylusPoints.Add(point);
                                        }
                                    }
                                    else
                                    {
                                        for (var i = 0; i <= n; i++)
                                        {
                                            var point = new StylusPoint();

                                            point.PressureFactor = (float)(0.4 * (n - i) / n + pressure);
                                            point.X = e.Stroke.StylusPoints[i].X;
                                            point.Y = e.Stroke.StylusPoints[i].Y;
                                            stylusPoints.Add(point);
                                        }
                                    }

                                    e.Stroke.StylusPoints = stylusPoints;
                                }
                                catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }

                            break;
                        case 3:
                            break;
                    }
                }
            }
            catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }

            Debug.WriteLine($"墨迹平滑检查: UseAdvancedBezierSmoothing={Settings.Canvas.UseAdvancedBezierSmoothing}, wasStraightened={wasStraightened}");
            Debug.WriteLine($"异步平滑设置: UseAsyncInkSmoothing={Settings.Canvas.UseAsyncInkSmoothing}, _inkSmoothingManager={_inkSmoothingManager != null}");

            if (Settings.Canvas.UseAdvancedBezierSmoothing && !wasStraightened)
            {
                try
                {
                    Debug.WriteLine($"开始墨迹平滑处理: 原始点数={e.Stroke.StylusPoints.Count}, 直线拉直={wasStraightened}");

                    if (inkCanvas.Strokes.Contains(e.Stroke))
                    {
                        if (Settings.Canvas.UseAsyncInkSmoothing && _inkSmoothingManager != null)
                        {
                            Debug.WriteLine("使用异步墨迹平滑");
                            _ = ProcessStrokeAsync(e.Stroke);
                        }
                        else
                        {
                            var smoothedStroke = _inkSmoothingManager?.SmoothStroke(e.Stroke) ?? e.Stroke;

                            if (smoothedStroke != e.Stroke)
                            {
                                // 补回平滑器未复制的 property data（激光/原生湿墨标记）。
                                InkSmoothingManager.CopyPropertyData(e.Stroke, smoothedStroke);
                                SetNewBackupOfStroke();
                                if (Settings.Canvas.MergeInkSmoothingWithUndo)
                                {
                                    _currentCommitType = CommitReason.CodeInput;
                                    inkCanvas.Strokes.Remove(e.Stroke);
                                    inkCanvas.Strokes.Add(smoothedStroke);
                                    timeMachine.TryReplaceLastUserInputHistory(new StrokeCollection { smoothedStroke });
                                    _currentCommitType = CommitReason.UserInput;
                                }
                                else
                                {
                                    _currentCommitType = CommitReason.ShapeRecognition;
                                    inkCanvas.Strokes.Remove(e.Stroke);
                                    inkCanvas.Strokes.Add(smoothedStroke);
                                    _currentCommitType = CommitReason.UserInput;
                                }
                                handwritingScheduleStroke = smoothedStroke;
                            }
                        }
                    }
                    else
                    {
                        Debug.WriteLine("原始笔画不在画布中，跳过平滑处理");
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"高级贝塞尔曲线平滑失败: {ex.Message}");
                }
            }
            else if (Settings.Canvas.FitToCurve && !wasStraightened)
            {
                drawingAttributes.FitToCurve = true;
            }

            return handwritingScheduleStroke;
        }

        /// <summary>
        /// 处理墨水画布的笔画收集事件
        /// </summary>
        /// <param name="sender">事件发送者</param>
        /// <param name="e">笔画收集事件参数</param>
        /// <remarks>
        /// 当用户在墨水画布上完成一笔绘制后：
        /// 1. 检查是否启用墨迹渐隐功能，如果启用则添加到墨迹渐隐管理器
        /// 2. 根据设置处理压感：
        ///    - 如果禁用压感，统一压感值为0.5
        ///    - 如果启用触摸压感模式，根据速度模拟压感
        /// 3. 如果启用了形状识别：
        ///    - 检查是否启用了直线自动拉直功能，如果是则尝试拉直线条
        ///    - 处理形状识别，包括圆形、椭圆、三角形、矩形等
        /// 4. 检查是否是压感笔书写，如果是则返回
        /// 5. 根据墨水风格设置模拟压感
        /// 6. 应用高级贝塞尔曲线平滑（仅在未进行直线拉直时）
        /// <para>
        /// 形状识别：IACore 典型用于 32 位进程；64 位可选用 WinRT（Windows 10+），详见设置「识别引擎」。
        /// </para>
        /// </remarks>
        private void inkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
        {
            if (e?.Stroke != null)
                ProcessCommittedStroke(e.Stroke);
        }

        /// <summary>
        /// Shared dry-ink post-process entry for WPF StrokeCollected and native wet-ink commit.
        /// Keeps fade / pressure / velocity tip / straighten / shape / handwriting / edge expand order.
        /// </summary>
        private void ProcessCommittedStroke(Stroke stroke)
        {
            if (stroke == null)
                return;

            var e = new InkCanvasStrokeCollectedEventArgs(stroke);
            var strokeDrawingAttributes = e.Stroke?.DrawingAttributes;

            // 手写识别输入在收笔尾部从最终画布 Stroke 复制，避免使用压感/平滑前快照。
            // Issue #286 — 边缘扩展画布提示：在笔画收集后立即判断位置。
            // HandleEdgeExpandHintAfterStroke 内部已做 eligible 检查与异常吞咽。
            try
            {
                if (e.Stroke?.StylusPoints != null && e.Stroke.StylusPoints.Count > 0)
                {
                    var strokePoints = new System.Collections.Generic.List<Point>(e.Stroke.StylusPoints.Count);
                    foreach (var sp in e.Stroke.StylusPoints) strokePoints.Add(sp.ToPoint());
                    HandleEdgeExpandHintAfterStroke(strokePoints);
                }
            }
            catch (Exception ex)
            {
                LogHelper.WriteLogToFile($"边缘扩展提示判定失败: {ex.Message}", LogHelper.LogType.Warning);
            }

            if (Settings.Canvas.EnableInkFade)
            {
                // 获取墨迹的起点和终点
                var startPoint = e.Stroke.StylusPoints.Count > 0 ? e.Stroke.StylusPoints[0].ToPoint() : new Point();
                var endPoint = e.Stroke.StylusPoints.Count > 0 ? e.Stroke.StylusPoints[e.Stroke.StylusPoints.Count - 1].ToPoint() : new Point();

                // Native freehand keeps physical EditingMode at None; do not re-enable WPF wet ink.
                EnsureNativePenPhysicalEditingMode();

                // 添加到墨迹渐隐管理器
                if (_inkFadeManager != null)
                {
                    if (penType == 2
                        && !e.Stroke.ContainsPropertyData(WpfStrokeCommitter.LaserRenderModeGuid))
                    {
                        e.Stroke.AddPropertyData(WpfStrokeCommitter.LaserRenderModeGuid, true);
                    }

                    long strokeDurationMs = 0;
                    if (_stylusDownTimestamp > 0)
                    {
                        strokeDurationMs = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - _stylusDownTimestamp;
                        _stylusDownTimestamp = 0;
                    }
                    _inkFadeManager.AddFadingStroke(e.Stroke, startPoint, endPoint, strokeDurationMs);
                }
                else
                {
                    LogHelper.WriteLogToFile("StrokeCollected: 墨迹渐隐管理器为空，无法添加墨迹", LogHelper.LogType.Error);
                }

                Dispatcher.BeginInvoke(new Action(() =>
                {
                    try
                    {
                        EnsureNativePenPhysicalEditingMode();

                        if (inkCanvas.Strokes.Contains(e.Stroke))
                        {
                            inkCanvas.Strokes.Remove(e.Stroke);
                        }
                    }
                    catch (Exception ex)
                    {
                        LogHelper.WriteLogToFile($"延迟移除墨迹时出错: {ex}", LogHelper.LogType.Error);
                    }
                }), DispatcherPriority.Background);

                return;
            }

            // 标记是否进行了直线拉直
            bool wasStraightened = false;
            Stroke strokeForHandwritingBeautify = null;

            if (Settings.Canvas.FitToCurve) drawingAttributes.FitToCurve = false;

            try
            {
                inkCanvas.Opacity = 1;
                var touchPressureSimulationApplied = false;

                // 原生湿墨管线提交的 Stroke：其 StylusPoint 压感已与实时预览一致。
                // 跳过所有会重写 PressureFactor 的干墨后处理（屏蔽压感归一化、触摸压感
                // 模拟、速度笔锋），否则抬笔瞬间线条粗细会跳变，产生“烘干闪变”。
                var isNativeWetInkCommitted =
                    e.Stroke != null
                    && e.Stroke.ContainsPropertyData(NativeWetInkCommittedGuid);

                if (isNativeWetInkCommitted)
                {
                    // 原生提交：保持 PressureFactor 不变。仍允许后续拉直/形状识别等不依赖
                    // 压感粗细的处理。
                }
                else if (Settings.Canvas.DisablePressure)
                {
                    var uniformPoints = new StylusPointCollection();
                    foreach (StylusPoint point in e.Stroke.StylusPoints)
                    {
                        StylusPoint newPoint = new StylusPoint(point.X, point.Y, 0.5f); // 统一压感值为0.5
                        uniformPoints.Add(newPoint);
                    }
                    e.Stroke.StylusPoints = uniformPoints;
                }
                else if (Settings.Canvas.EnablePressureTouchMode)
                {
                    bool isTouchInput = true;
                    foreach (StylusPoint point in e.Stroke.StylusPoints)
                    {
                        if ((point.PressureFactor > 0.501 || point.PressureFactor < 0.5) && point.PressureFactor != 0)
                        {
                            isTouchInput = false;
                            break;
                        }
                    }

                    if (isTouchInput)
                    {
                        switch (Settings.Canvas.InkStyle)
                        {
                            case 1:
                                if (penType == 0)
                                    try
                                    {
                                        var stylusPoints = new StylusPointCollection();
                                        var n = e.Stroke.StylusPoints.Count - 1;

                                        for (var i = 0; i <= n; i++)
                                        {
                                            var speed = GetPointSpeed(e.Stroke.StylusPoints[Math.Max(i - 1, 0)].ToPoint(),
                                                e.Stroke.StylusPoints[i].ToPoint(),
                                                e.Stroke.StylusPoints[Math.Min(i + 1, n)].ToPoint());
                                            var point = new StylusPoint
                                            {
                                                PressureFactor = RateBasedPressureFactorFromPointSpeed(speed),
                                                X = e.Stroke.StylusPoints[i].X,
                                                Y = e.Stroke.StylusPoints[i].Y
                                            };
                                            stylusPoints.Add(point);
                                        }

                                        touchPressureSimulationApplied = true;
                                        e.Stroke.StylusPoints = stylusPoints;
                                    }
                                    catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
                                break;
                            case 0:
                                if (penType == 0)
                                    try
                                    {
                                        var stylusPoints = new StylusPointCollection();
                                        var n = e.Stroke.StylusPoints.Count - 1;
                                        var pressure = 0.1;
                                        var x = 10;
                                        if (n == 1) return;
                                        if (n >= x)
                                        {
                                            for (var i = 0; i < n - x; i++)
                                            {
                                                var point = new StylusPoint();

                                                point.PressureFactor = (float)0.5;
                                                point.X = e.Stroke.StylusPoints[i].X;
                                                point.Y = e.Stroke.StylusPoints[i].Y;
                                                stylusPoints.Add(point);
                                            }

                                            for (var i = n - x; i <= n; i++)
                                            {
                                                var point = new StylusPoint();

                                                point.PressureFactor = (float)((0.5 - pressure) * (n - i) / x + pressure);
                                                point.X = e.Stroke.StylusPoints[i].X;
                                                point.Y = e.Stroke.StylusPoints[i].Y;
                                                stylusPoints.Add(point);
                                            }
                                        }
                                        else
                                        {
                                            for (var i = 0; i <= n; i++)
                                            {
                                                var point = new StylusPoint();

                                                point.PressureFactor = (float)(0.4 * (n - i) / n + pressure);
                                                point.X = e.Stroke.StylusPoints[i].X;
                                                point.Y = e.Stroke.StylusPoints[i].Y;
                                                stylusPoints.Add(point);
                                            }
                                        }

                                        touchPressureSimulationApplied = true;
                                        e.Stroke.StylusPoints = stylusPoints;
                                    }
                                    catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
                                break;
                        }
                    }
                }

                // 实时笔锋：勿依赖 DrawingAttributes.IgnorePressure。无压感触摸/鼠标等设备上，运行时仍可能为 true，
                // 会导致不进入逻辑或进入后渲染仍忽略 PressureFactor；具体在 ApplyVelocityBrushTipFromSpeed 内关闭。
                // 「屏蔽压感」时必须跳过：否则会重写 PressureFactor 并强制 IgnorePressure=false，与归一压感冲突。
                // VelocityBrushTipMix <= 0 时 ApplyVelocityBrushTipFromSpeed 为空操作，无需调用。
                if (Settings.Canvas.InkStyle == 3
                    && Settings.Canvas.VelocityBrushTipMix > 0
                    && !Settings.Canvas.DisablePressure
                    && !touchPressureSimulationApplied
                    && penType != 1
                    && e.Stroke?.DrawingAttributes != null
                    && !e.Stroke.DrawingAttributes.IsHighlighter
                    && !e.Stroke.ContainsPropertyData(RealtimeVelocityBrushTipAppliedGuid)
                    && e.Stroke.StylusPoints.Count >= 3)
                {
                    ApplyVelocityBrushTipFromSpeed(e.Stroke);
                }

                // Apply line straightening and endpoint snapping if ink-to-shape is enabled

                Stroke straightStrokeForHandwritingKey = null;

                if (Settings.InkToShape.IsInkToShapeEnabled)
                {
                    // 检查是否启用了直线自动拉直功能
                    if (Settings.Canvas.AutoStraightenLine && IsPotentialStraightLine(e.Stroke))
                    {
                        Point endpoint1, endpoint2;
                        bool shouldStraighten = TryGetStraightLineEndpoints(e.Stroke, out endpoint1, out endpoint2);

                        if (shouldStraighten)
                        {
                            Point startPoint = endpoint1;
                            Point endPoint = endpoint2;

                            // 只有当确定要拉直线条时，才检查端点吸附
                            if (Settings.Canvas.LineEndpointSnapping)
                            {
                                // 只有在启用了形状识别（矩形或三角形）时才执行端点吸附
                                if (Settings.InkToShape.IsInkToShapeRectangle || Settings.InkToShape.IsInkToShapeTriangle)
                                {
                                    Point[] snappedPoints = GetSnappedEndpoints(startPoint, endPoint);
                                    if (snappedPoints != null)
                                    {
                                        startPoint = snappedPoints[0];
                                        endPoint = snappedPoints[1];
                                    }
                                }
                            }

                            // 创建直线
                            StylusPointCollection straightLinePoints = CreateStraightLine(startPoint, endPoint);
                            Stroke straightStroke = new Stroke(straightLinePoints)
                            {
                                DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
                            };
                            straightStroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);

                            // Replace the original stroke with the straightened one
                            ReplaceStrokesSafely(null, straightStroke, e.Stroke);

                            straightStrokeForHandwritingKey = straightStroke;

                            // We can't modify e.Stroke directly, but we need to update newStrokes
                            // to ensure proper shape recognition for the straightened line
                            if (newStrokes.Contains(e.Stroke))
                            {
                                newStrokes.Remove(e.Stroke);
                                newStrokes.Add(straightStroke);
                            }

                            wasStraightened = true; // 标记已进行直线拉直
                        }
                    }
                }

                strokeForHandwritingBeautify = e.Stroke;
                if (wasStraightened && straightStrokeForHandwritingKey != null)
                    strokeForHandwritingBeautify = straightStrokeForHandwritingKey;
                else if (wasStraightened && inkCanvas.Strokes.Count > 0)
                    strokeForHandwritingBeautify = inkCanvas.Strokes[inkCanvas.Strokes.Count - 1];


                if (ShapeRecognitionRouter.ShouldRunShapeRecognition(
                        Settings.InkToShape.IsInkToShapeEnabled,
                        ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine)))
                {
                    async Task InkToShapeProcessCoreAsync()
                    {
                        await InkToShapeSerial.WaitAsync().ConfigureAwait(true);
                        try
                        {
                            newStrokes.Add(e.Stroke);
                            if (newStrokes.Count > 4) newStrokes.RemoveAt(0);
                            for (var i = 0; i < newStrokes.Count; i++)
                                if (!inkCanvas.Strokes.Contains(newStrokes[i]))
                                    newStrokes.RemoveAt(i--);

                            for (var i = 0; i < circles.Count; i++)
                                if (!inkCanvas.Strokes.Contains(circles[i].Stroke))
                                    circles.RemoveAt(i);

                            // 处理矩形参考线系统
                            ProcessRectangleGuideLines(e.Stroke);

                            var shapeMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine);
                            InkShapeRecognitionResult result = InkShapeRecognitionResult.Empty;

                            if (ShapeRecognitionRouter.ResolveUseWinRt(shapeMode) && Helpers.WinRtInkShapeRecognizer.IsApiAvailable)
                            {
                                result = await ModernInkAnalyzer.AnalyzeAsync(newStrokes);
                            }
                            else
                            {
                                var strokeReco = new StrokeCollection();
                                result = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(newStrokes, shapeMode);
                                for (var i = newStrokes.Count - 1; i >= 0; i--)
                                {
                                    strokeReco.Add(newStrokes[i]);
                                    var newResult = await InkRecognizeHelper.RecognizeShapeUnifiedAsync(strokeReco, shapeMode);
                                    if (newResult.IsSuccess &&
                                        (newResult.ShapeName == "Circle" || newResult.ShapeName == "Ellipse"))
                                    {
                                        result = newResult;
                                        break;
                                    }
                                }
                            }

                            if (!result.IsSuccess)
                                return;

                            // 拟合度门禁：吻合度过低保留原笔画，不强行纠正（涂鸦误判为形状的主修复点）
                            if (!PassesShapeFitGate(result))
                                return;

                            if (result.ShapeName == "Circle" &&
                                Settings.InkToShape.IsInkToShapeRounded)
                            {
                                if (result.ShapeWidth > 75 * GetResolutionScale())
                                {
                                    foreach (var circle in circles)
                                        //判断是否画同心圆
                                        if (Math.Abs(result.Centroid.X - circle.Centroid.X) / result.ShapeWidth < 0.12 &&
                                            Math.Abs(result.Centroid.Y - circle.Centroid.Y) / result.ShapeWidth < 0.12)
                                        {
                                            result.Centroid = circle.Centroid;
                                            break;
                                        }
                                        else
                                        {
                                            var d = (result.Centroid.X - circle.Centroid.X) *
                                                    (result.Centroid.X - circle.Centroid.X) +
                                                    (result.Centroid.Y - circle.Centroid.Y) *
                                                    (result.Centroid.Y - circle.Centroid.Y);
                                            d = Math.Sqrt(d);
                                            //判断是否画外切圆
                                            var x = result.ShapeWidth / 2.0 + circle.R - d;
                                            if (Math.Abs(x) / result.ShapeWidth < 0.1)
                                            {
                                                var sinTheta = (result.Centroid.Y - circle.Centroid.Y) / d;
                                                var cosTheta = (result.Centroid.X - circle.Centroid.X) / d;
                                                var newX = result.Centroid.X + x * cosTheta;
                                                var newY = result.Centroid.Y + x * sinTheta;
                                                result.Centroid = new Point(newX, newY);
                                            }

                                            //判断是否画外切圆
                                            x = Math.Abs(circle.R - result.ShapeWidth / 2.0) - d;
                                            if (Math.Abs(x) / result.ShapeWidth < 0.1)
                                            {
                                                var sinTheta = (result.Centroid.Y - circle.Centroid.Y) / d;
                                                var cosTheta = (result.Centroid.X - circle.Centroid.X) / d;
                                                var newX = result.Centroid.X + x * cosTheta;
                                                var newY = result.Centroid.Y + x * sinTheta;
                                                result.Centroid = new Point(newX, newY);
                                            }
                                        }

                                    var iniP = new Point(result.Centroid.X - result.ShapeWidth / 2,
                                        result.Centroid.Y - result.ShapeHeight / 2);
                                    var endP = new Point(result.Centroid.X + result.ShapeWidth / 2,
                                        result.Centroid.Y + result.ShapeHeight / 2);
                                    var pointList = GenerateEllipseGeometry(iniP, endP);
                                    var point = new StylusPointCollection(pointList);
                                    var stroke = new Stroke(point)
                                    {
                                        DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
                                    };
                                    stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
                                    circles.Add(new Circle(result.Centroid, result.ShapeWidth / 2.0, stroke));
                                    ReplaceStrokesSafely(result.StrokesToRemove, stroke, e.Stroke);
                                    newStrokes = new StrokeCollection();
                                }
                            }
                            else if (result.ShapeName.Contains("Ellipse") &&
                                     Settings.InkToShape.IsInkToShapeRounded)
                            {
                                var p = result.HotPoints;
                                var a = GetDistance(p[0], p[2]) / 2; //长半轴
                                var b = GetDistance(p[1], p[3]) / 2; //短半轴
                                if (a < b)
                                {
                                    var t = a;
                                    a = b;
                                    b = t;
                                }

                                result.Centroid = new Point((p[0].X + p[2].X) / 2, (p[0].Y + p[2].Y) / 2);
                                var needRotation = true;

                                if (result.ShapeWidth > 75 * GetResolutionScale() || (result.ShapeHeight > 75 * GetResolutionScale() && p.Count == 4))
                                {
                                    var iniP = new Point(result.Centroid.X - result.ShapeWidth / 2,
                                        result.Centroid.Y - result.ShapeHeight / 2);
                                    var endP = new Point(result.Centroid.X + result.ShapeWidth / 2,
                                        result.Centroid.Y + result.ShapeHeight / 2);

                                    // WinRT 返回的热点顺序/方向不稳定时，用点集反推 IACore 风格椭圆参数（中心/长短轴/方向/四个端点）
                                    var hasEllipseParams = TryEstimateEllipseParamsFromStrokes(
                                        result.StrokesToRemove,
                                        out var ellipseCentroid,
                                        out var ellipseA,
                                        out var ellipseB,
                                        out var ellipseThetaRad,
                                        out var ellipseMajor0,
                                        out var ellipseMajor1,
                                        out var ellipseMinor0,
                                        out var ellipseMinor1);

                                    foreach (var circle in circles)
                                        //判断是否画同心椭圆
                                        if (Math.Abs(result.Centroid.X - circle.Centroid.X) / a < 0.2 &&
                                            Math.Abs(result.Centroid.Y - circle.Centroid.Y) / a < 0.2)
                                        {
                                            result.Centroid = circle.Centroid;
                                            iniP = new Point(result.Centroid.X - result.ShapeWidth / 2,
                                                result.Centroid.Y - result.ShapeHeight / 2);
                                            endP = new Point(result.Centroid.X + result.ShapeWidth / 2,
                                                result.Centroid.Y + result.ShapeHeight / 2);

                                            //再判断是否与圆相切
                                            if (Math.Abs(a - circle.R) / a < 0.2)
                                            {
                                                if (result.ShapeWidth >= result.ShapeHeight)
                                                {
                                                    iniP.X = result.Centroid.X - circle.R;
                                                    endP.X = result.Centroid.X + circle.R;
                                                    iniP.Y = result.Centroid.Y - b;
                                                    endP.Y = result.Centroid.Y + b;
                                                }
                                                else
                                                {
                                                    iniP.Y = result.Centroid.Y - circle.R;
                                                    endP.Y = result.Centroid.Y + circle.R;
                                                    iniP.X = result.Centroid.X - a;
                                                    endP.X = result.Centroid.X + a;
                                                }
                                            }

                                            break;
                                        }
                                        else if (Math.Abs(result.Centroid.X - circle.Centroid.X) / a < 0.2)
                                        {
                                            var sinTheta = Math.Abs(circle.Centroid.Y - result.Centroid.Y) /
                                                           circle.R;
                                            var cosTheta = Math.Sqrt(1 - sinTheta * sinTheta);
                                            var newA = circle.R * cosTheta;
                                            if (circle.R * sinTheta / circle.R < 0.9 && a / b > 2 &&
                                                Math.Abs(newA - a) / newA < 0.3)
                                            {
                                                iniP.X = circle.Centroid.X - newA;
                                                endP.X = circle.Centroid.X + newA;
                                                iniP.Y = result.Centroid.Y - newA / 5;
                                                endP.Y = result.Centroid.Y + newA / 5;

                                                var topB = endP.Y - iniP.Y;

                                                newStrokes = new StrokeCollection();

                                                var _pointList = GenerateEllipseGeometry(iniP, endP, false);
                                                var _point = new StylusPointCollection(_pointList);
                                                var _stroke = new Stroke(_point)
                                                {
                                                    DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
                                                };
                                                _stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
                                                var _dashedLineStroke =
                                                    GenerateDashedLineEllipseStrokeCollection(iniP, endP, true, false);
                                                var strokes = new StrokeCollection {
                                                    _stroke,
                                                    _dashedLineStroke
                                                };
                                                ReplaceStrokesSafely(result.StrokesToRemove, strokes, e.Stroke);
                                                return;
                                            }
                                        }
                                        else if (Math.Abs(result.Centroid.Y - circle.Centroid.Y) / a < 0.2)
                                        {
                                            var cosTheta = Math.Abs(circle.Centroid.X - result.Centroid.X) /
                                                           circle.R;
                                            var sinTheta = Math.Sqrt(1 - cosTheta * cosTheta);
                                            var newA = circle.R * sinTheta;
                                            if (circle.R * sinTheta / circle.R < 0.9 && a / b > 2 &&
                                                Math.Abs(newA - a) / newA < 0.3)
                                            {
                                                iniP.X = result.Centroid.X - newA / 5;
                                                endP.X = result.Centroid.X + newA / 5;
                                                iniP.Y = circle.Centroid.Y - newA;
                                                endP.Y = circle.Centroid.Y + newA;
                                                needRotation = false;
                                            }
                                        }

                                    // 用反推参数替换中心与长短轴（比 WinRT 的包围盒更接近 IACore，且不会竖横翻转）
                                    if (hasEllipseParams)
                                    {
                                        result.Centroid = ellipseCentroid;
                                        a = ellipseA;
                                        b = ellipseB;
                                        iniP = new Point(result.Centroid.X - a, result.Centroid.Y - b);
                                        endP = new Point(result.Centroid.X + a, result.Centroid.Y + b);
                                        // 用端点重写热点，保证后续回退分支也一致
                                        p = new PointCollection { ellipseMajor0, ellipseMinor0, ellipseMajor1, ellipseMinor1 };
                                    }

                                    var pointList = GenerateEllipseGeometry(iniP, endP);
                                    var point = new StylusPointCollection(pointList);
                                    var stroke = new Stroke(point)
                                    {
                                        DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
                                    };
                                    stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);

                                    if (needRotation)
                                    {
                                        var m = new Matrix();
                                        // 优先使用反推参数角度；否则用端点向量角度（使用 Atan2 避免斜率无穷）
                                        var theta = hasEllipseParams ? ellipseThetaRad : Math.Atan2(p[2].Y - p[0].Y, p[2].X - p[0].X);
                                        m.RotateAt(theta * 180.0 / Math.PI, result.Centroid.X, result.Centroid.Y);
                                        stroke.Transform(m, false);
                                    }

                                    ReplaceStrokesSafely(result.StrokesToRemove, stroke, e.Stroke);
                                    GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
                                    newStrokes = new StrokeCollection();
                                }
                            }
                            else if (result.ShapeName.Contains("Triangle") &&
                                     Settings.InkToShape.IsInkToShapeTriangle)
                            {
                                var p = result.HotPoints;
                                if ((Math.Max(Math.Max(p[0].X, p[1].X), p[2].X) -
                                     Math.Min(Math.Min(p[0].X, p[1].X), p[2].X) >= 100 * GetResolutionScale() ||
                                     Math.Max(Math.Max(p[0].Y, p[1].Y), p[2].Y) -
                                     Math.Min(Math.Min(p[0].Y, p[1].Y), p[2].Y) >= 100 * GetResolutionScale()) &&
                                    result.HotPoints.Count == 3)
                                {
                                    //纠正垂直与水平关系
                                    var newPoints = FixPointsDirection(p[0], p[1]);
                                    p[0] = newPoints[0];
                                    p[1] = newPoints[1];
                                    newPoints = FixPointsDirection(p[0], p[2]);
                                    p[0] = newPoints[0];
                                    p[2] = newPoints[1];
                                    newPoints = FixPointsDirection(p[1], p[2]);
                                    p[1] = newPoints[0];
                                    p[2] = newPoints[1];

                                    var pointList = p.ToList();
                                    //pointList.Add(p[0]);
                                    var point = new StylusPointCollection(pointList);
                                    var stroke = new Stroke(GenerateFakePressureTriangle(point))
                                    {
                                        DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
                                    };
                                    stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
                                    ReplaceStrokesSafely(result.StrokesToRemove, stroke, e.Stroke);
                                    GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
                                    newStrokes = new StrokeCollection();
                                }
                            }
                            else if ((result.ShapeName.Contains("Rectangle") ||
                                      result.ShapeName.Contains("Diamond") ||
                                      result.ShapeName.Contains("Parallelogram") ||
                                      result.ShapeName.Contains("Square") ||
                                      result.ShapeName.Contains("Trapezoid") ||
                                      result.ShapeName.Contains("Quadrilateral")) &&
                                     Settings.InkToShape.IsInkToShapeRectangle)
                            {
                                var p = result.HotPoints;
                                if ((Math.Max(Math.Max(Math.Max(p[0].X, p[1].X), p[2].X), p[3].X) -
                                     Math.Min(Math.Min(Math.Min(p[0].X, p[1].X), p[2].X), p[3].X) >= 100 * GetResolutionScale() ||
                                     Math.Max(Math.Max(Math.Max(p[0].Y, p[1].Y), p[2].Y), p[3].Y) -
                                     Math.Min(Math.Min(Math.Min(p[0].Y, p[1].Y), p[2].Y), p[3].Y) >= 100 * GetResolutionScale()) &&
                                    result.HotPoints.Count == 4)
                                {
                                    //纠正垂直与水平关系
                                    var newPoints = FixPointsDirection(p[0], p[1]);
                                    p[0] = newPoints[0];
                                    p[1] = newPoints[1];
                                    newPoints = FixPointsDirection(p[1], p[2]);
                                    p[1] = newPoints[0];
                                    p[2] = newPoints[1];
                                    newPoints = FixPointsDirection(p[2], p[3]);
                                    p[2] = newPoints[0];
                                    p[3] = newPoints[1];
                                    newPoints = FixPointsDirection(p[3], p[0]);
                                    p[3] = newPoints[0];
                                    p[0] = newPoints[1];

                                    var pointList = p.ToList();
                                    pointList.Add(p[0]);
                                    var point = new StylusPointCollection(pointList);
                                    var stroke = new Stroke(GenerateFakePressureRectangle(point))
                                    {
                                        DrawingAttributes = inkCanvas.DefaultDrawingAttributes.Clone()
                                    };
                                    stroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);
                                    ReplaceStrokesSafely(result.StrokesToRemove, stroke, e.Stroke);
                                    GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed;
                                    newStrokes = new StrokeCollection();
                                }
                            }
                        }
                        catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }
                        finally
                        {
                            InkToShapeSerial.Release();
                        }
                    }

                    bool InkToShapeProcess()
                    {
                        // WinRT 与非 WinRT 均延后到 Dispatcher 上异步执行：在 StrokeCollected 内对
                        // InkToShapeProcessCoreAsync 同步 GetResult() 会长时间阻塞 UI 线程（抬笔卡顿）；
                        // WinRT 路径若同步等待还可能与贴回 UI 的延续死锁（见类注释 InkToShapeSerial）。
                        var strokeHw = strokeForHandwritingBeautify;
                        var wsTail = wasStraightened;
                        // ApplicationIdle：在更高优先级（布局/输入/本帧渲染）之后执行，减轻抬笔瞬间主线程“顶死”感。
                        Dispatcher.BeginInvoke(new Action(async () =>
                        {
                            try
                            {
                                await InkToShapeProcessCoreAsync();
                                var strokeAfterTail = RunStrokeCollectedPostShapeRecognitionTail(e, wsTail);
                                if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
                                {
                                    var canvasStrokeForHw = wsTail ? strokeHw : strokeAfterTail;
                                    ScheduleHandwritingGlyphReplaceAfterStrokeCollected(canvasStrokeForHw);
                                }
                            }
                            catch (Exception ex)
                            {
                                System.Diagnostics.Debug.WriteLine(ex);
                            }
                        }), DispatcherPriority.ApplicationIdle);
                        return true;
                    }

                    if (InkToShapeProcess())
                        return;
                }
            }
            catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); }

            var strokeAfterTailSync = RunStrokeCollectedPostShapeRecognitionTail(e, wasStraightened);
            if (Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify
                && !ShapeRecognitionRouter.ShouldRunShapeRecognition(
                    Settings.InkToShape.IsInkToShapeEnabled,
                    ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine)))
            {
                var canvasStrokeForHw = wasStraightened ? strokeForHandwritingBeautify : strokeAfterTailSync;
                ScheduleHandwritingGlyphReplaceAfterStrokeCollected(canvasStrokeForHw);
            }
        }

        /// <summary>
        /// 异步处理笔画平滑
        /// </summary>
        /// <param name="originalStroke">原始笔画</param>
        /// <returns>返回一个表示异步操作的Task</returns>
        /// <remarks>
        /// 异步处理笔画平滑的流程：
        /// 1. 调用墨迹平滑管理器的SmoothStrokeAsync方法
        /// 2. 在平滑完成后，在UI线程上执行笔画替换
        /// 3. 如果原始笔画仍然存在于画布中且平滑后的笔画不同，则替换原始笔画
        /// 4. 捕获并记录可能的异常
        /// </remarks>
        private async Task ProcessStrokeAsync(Stroke originalStroke)
        {
            try
            {
                Debug.WriteLine($"异步平滑开始: 原始点数={originalStroke.StylusPoints.Count}");
                await _inkSmoothingManager.SmoothStrokeAsync(originalStroke, (original, smoothed) =>
                {
                    Debug.WriteLine($"异步平滑完成: 原始点数={original.StylusPoints.Count}, 平滑后点数={smoothed.StylusPoints.Count}");
                    Debug.WriteLine($"墨迹比较: smoothed != original = {smoothed != original}");
                    Debug.WriteLine($"画布包含原始墨迹: {inkCanvas.Strokes.Contains(original)}");

                    // 在UI线程上执行笔画替换
                    if (inkCanvas.Strokes.Contains(original) && smoothed != original)
                    {
                        Debug.WriteLine("异步替换原始笔画为平滑后的笔画");
                        // 平滑器创建的新 Stroke 只克隆 DrawingAttributes，需补回 property data
                        //（LaserRenderModeGuid / NativeWetInkCommittedGuid 等，否则激光笔迹失效）。
                        InkSmoothingManager.CopyPropertyData(original, smoothed);
                        SetNewBackupOfStroke();
                        if (Settings.Canvas.MergeInkSmoothingWithUndo)
                        {
                            _currentCommitType = CommitReason.CodeInput;
                            inkCanvas.Strokes.Remove(original);
                            inkCanvas.Strokes.Add(smoothed);
                            timeMachine.TryReplaceLastUserInputHistory(new StrokeCollection { smoothed });
                            _currentCommitType = CommitReason.UserInput;
                        }
                        else
                        {
                            _currentCommitType = CommitReason.ShapeRecognition;
                            inkCanvas.Strokes.Remove(original);
                            inkCanvas.Strokes.Add(smoothed);
                            _currentCommitType = CommitReason.UserInput;
                        }
                        // 平滑后的识别快照由回调在画布替换完成后生成，避免防抖线程读取平滑前的旧点。
                        MigrateHandwritingBeautifyCanvasStrokeReference(original, smoothed);
                        UpdateHandwritingBeautifyRecognitionSnapshot(smoothed);
                    }
                    else
                    {
                        Debug.WriteLine($"异步平滑后的笔画与原始笔画相同，未进行替换 (contains={inkCanvas.Strokes.Contains(original)}, different={smoothed != original})");
                    }
                });
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"异步墨迹平滑失败: {ex.Message}");
            }
        }

        /// <summary>
        /// 检查一笔墨迹是否可能是直线
        /// </summary>
        /// <param name="stroke">要检查的笔画</param>
        /// <returns>如果可能是直线则返回true，否则返回false</returns>
        /// <remarks>
        /// 检查一笔墨迹是否可能是直线的流程：
        /// 1. 确保有足够的点来进行线条分析（至少5个点）
        /// 2. 计算线条长度，确保线条足够长（使用分辨率自适应阈值）
        /// 3. 检查墨迹复杂度，避免将复杂图形拉直
        /// 4. 检查是否为明显的曲线
        /// 5. 根据用户设置的灵敏度值计算阈值
        /// 6. 快速检查：计算几个关键点与直线的距离
        /// 7. 根据偏差阈值判断是否可能是直线
        /// </remarks>
        private bool IsPotentialStraightLine(Stroke stroke)
        {
            // 确保有足够的点来进行线条分析
            if (stroke.StylusPoints.Count < 5)
                return false;

            Point start = stroke.StylusPoints.First().ToPoint();
            Point end = stroke.StylusPoints.Last().ToPoint();
            double lineLength = GetDistance(start, end);
            // 分辨率自适应阈值
            double adaptiveThreshold = Settings.Canvas.AutoStraightenLineThreshold * GetResolutionScale();
            // 线条必须足够长才考虑拉直，使用自适应阈值
            if (lineLength < adaptiveThreshold)
                return false;

            // 新增：检查墨迹复杂度，避免将复杂图形拉直
            if (IsComplexShape(stroke))
                return false;

            // 新增：检查是否为明显的曲线
            if (IsObviousCurve(stroke))
                return false;

            // 获取用户设置的灵敏度值，确保使用正确的设置
            double sensitivity = Settings.InkToShape.LineStraightenSensitivity;

            // 输出当前灵敏度值
            Debug.WriteLine($"IsPotentialStraightLine - sensitivity: {sensitivity}, length: {lineLength}");

            // 将灵敏度转换为阈值：灵敏度0.05-2.0映射到阈值0.01-0.4
            double quickThreshold = Math.Max(0.01, sensitivity * 0.2); // 确保最小阈值为0.01

            Debug.WriteLine($"使用快速检查阈值: {quickThreshold}");

            // 快速检查：计算几个关键点与直线的距离
            if (stroke.StylusPoints.Count >= 10)
            {
                List<Point> checkPoints;

                // 使用采样点进行更准确的判断
                if (Settings.Canvas.HighPrecisionLineStraighten)
                {
                    var allPoints = stroke.StylusPoints.Select(p => p.ToPoint()).ToList();
                    checkPoints = SamplePointsByDistance(allPoints, 10.0);
                    Debug.WriteLine($"高精度模式快速检查：原始点数={allPoints.Count}, 采样点数={checkPoints.Count}");
                }
                else
                {
                    // 取中点和1/4、3/4位置的点
                    int quarterIdx = stroke.StylusPoints.Count / 4;
                    int midIdx = stroke.StylusPoints.Count / 2;
                    int threeQuarterIdx = quarterIdx * 3;

                    checkPoints = new List<Point>
                    {
                        stroke.StylusPoints[quarterIdx].ToPoint(),
                        stroke.StylusPoints[midIdx].ToPoint(),
                        stroke.StylusPoints[threeQuarterIdx].ToPoint()
                    };
                }

                // 计算所有检查点与直线的平均偏差
                double totalDeviation = 0;
                double maxDeviation = 0;
                int validPointCount = 0;

                foreach (Point checkPoint in checkPoints)
                {
                    double deviation = DistanceFromLineToPoint(start, end, checkPoint);
                    totalDeviation += deviation;
                    maxDeviation = Math.Max(maxDeviation, deviation);
                    validPointCount++;
                }

                if (validPointCount > 0)
                {
                    double avgDeviation = totalDeviation / validPointCount;
                    // 使用相对偏差：偏差与线长的比例，并使用灵敏度进行调整
                    double quickRelativeThreshold = lineLength * quickThreshold;

                    // 使用平均偏差和最大偏差的综合判断
                    double deviationThreshold = Settings.Canvas.HighPrecisionLineStraighten
                        ? Math.Max(avgDeviation, maxDeviation * 0.7) // 高精度模式更严格
                        : maxDeviation;

                    // 记录检测到的偏差
                    Debug.WriteLine($"Deviations: avg={avgDeviation:F2}, max={maxDeviation:F2}, threshold={quickRelativeThreshold:F2}, highPrecision={Settings.Canvas.HighPrecisionLineStraighten}");

                    if (deviationThreshold > quickRelativeThreshold)
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// 检查墨迹是否为复杂形状
        /// </summary>
        /// <param name="stroke">要检查的笔画</param>
        /// <returns>如果是复杂形状则返回true，否则返回false</returns>
        /// <remarks>
        /// 检查墨迹是否为复杂形状的流程：
        /// 1. 确保有足够的点来进行分析（至少10个点）
        /// 2. 计算直线距离和实际路径长度
        /// 3. 如果实际路径长度远大于直线距离（2.5倍以上），说明是复杂形状
        /// 4. 检查方向变化次数，如果超过动态阈值，说明是复杂形状
        /// </remarks>
        private bool IsComplexShape(Stroke stroke)
        {
            if (stroke.StylusPoints.Count < 10) return false;

            Point start = stroke.StylusPoints.First().ToPoint();
            Point end = stroke.StylusPoints.Last().ToPoint();
            double lineLength = GetDistance(start, end);

            // 计算墨迹的实际路径长度
            double actualLength = 0;
            for (int i = 1; i < stroke.StylusPoints.Count; i++)
            {
                Point p1 = stroke.StylusPoints[i - 1].ToPoint();
                Point p2 = stroke.StylusPoints[i].ToPoint();
                actualLength += GetDistance(p1, p2);
            }

            // 如果实际路径长度远大于直线距离，说明是复杂形状
            double complexityRatio = actualLength / Math.Max(lineLength, 1);
            if (complexityRatio > 2.5) // 实际路径是直线距离的2.5倍以上
            {
                Debug.WriteLine($"检测到复杂形状：复杂度比率 = {complexityRatio:F2}");
                return true;
            }

            // 检查方向变化次数
            int directionChanges = CountDirectionChanges(stroke);
            int maxAllowedChanges = Math.Max(3, stroke.StylusPoints.Count / 20); // 动态阈值
            if (directionChanges > maxAllowedChanges)
            {
                Debug.WriteLine($"检测到复杂形状：方向变化次数 = {directionChanges}，阈值 = {maxAllowedChanges}");
                return true;
            }

            return false;
        }

        /// <summary>
        /// 检查是否为明显的曲线（如圆弧、抛物线等）
        /// </summary>
        /// <param name="stroke">要检查的笔画</param>
        /// <returns>如果是明显的曲线则返回true，否则返回false</returns>
        /// <remarks>
        /// 检查墨迹是否为明显的曲线的流程：
        /// 1. 确保有足够的点来进行分析（至少10个点）
        /// 2. 计算线条长度
        /// 3. 检查曲率一致性，如果一致则认为是明显的曲线
        /// 4. 检查中点偏移（对圆弧特别有效）：
        ///    - 计算中点到直线的距离
        ///    - 如果中点偏移超过线长的15%，且偏移方向一致，可能是圆弧
        /// </remarks>
        private bool IsObviousCurve(Stroke stroke)
        {
            if (stroke.StylusPoints.Count < 10) return false;

            Point start = stroke.StylusPoints.First().ToPoint();
            Point end = stroke.StylusPoints.Last().ToPoint();
            double lineLength = GetDistance(start, end);

            // 检查曲率一致性
            if (HasConsistentCurvature(stroke))
            {
                Debug.WriteLine("检测到明显曲线：曲率一致");
                return true;
            }

            // 检查中点偏移（对圆弧特别有效）
            int midIndex = stroke.StylusPoints.Count / 2;
            Point midPoint = stroke.StylusPoints[midIndex].ToPoint();
            double midDeviation = DistanceFromLineToPoint(start, end, midPoint);

            // 如果中点偏移超过线长的15%，且偏移方向一致，可能是圆弧
            if (midDeviation > lineLength * 0.15)
            {
                // 检查偏移方向的一致性
                if (IsConsistentArcDirection(stroke))
                {
                    Debug.WriteLine($"检测到明显曲线：中点偏移 = {midDeviation:F2}，线长 = {lineLength:F2}");
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 计算方向变化次数
        /// </summary>
        /// <param name="stroke">要检查的笔画</param>
        /// <returns>返回方向变化的次数</returns>
        /// <remarks>
        /// 计算方向变化次数的流程：
        /// 1. 确保有足够的点来进行分析（至少3个点）
        /// 2. 遍历笔画中的每个点（除了第一个和最后一个）
        /// 3. 计算每个点前后线段的角度变化
        /// 4. 处理角度跨越问题（超过180度的情况）
        /// 5. 如果角度变化超过30度，且与上一次角度变化的差异超过15度，认为是方向变化
        /// 6. 返回方向变化的总次数
        /// </remarks>
        private int CountDirectionChanges(Stroke stroke)
        {
            if (stroke.StylusPoints.Count < 3) return 0;

            int changes = 0;
            double lastAngle = 0;
            bool hasLastAngle = false;

            for (int i = 1; i < stroke.StylusPoints.Count - 1; i++)
            {
                Point p1 = stroke.StylusPoints[i - 1].ToPoint();
                Point p2 = stroke.StylusPoints[i].ToPoint();
                Point p3 = stroke.StylusPoints[i + 1].ToPoint();

                // 计算角度变化
                double angle1 = Math.Atan2(p2.Y - p1.Y, p2.X - p1.X);
                double angle2 = Math.Atan2(p3.Y - p2.Y, p3.X - p2.X);
                double angleDiff = Math.Abs(angle2 - angle1);

                // 处理角度跨越问题
                if (angleDiff > Math.PI) angleDiff = 2 * Math.PI - angleDiff;

                // 如果角度变化超过30度，认为是方向变化
                if (angleDiff > Math.PI / 6) // 30度
                {
                    if (hasLastAngle && Math.Abs(angleDiff - lastAngle) > Math.PI / 12) // 15度
                    {
                        changes++;
                    }
                    lastAngle = angleDiff;
                    hasLastAngle = true;
                }
            }

            return changes;
        }

        /// <summary>
        /// 检查曲率是否一致（用于识别圆弧等规则曲线）
        /// </summary>
        /// <param name="stroke">要检查的笔画</param>
        /// <returns>如果曲率一致则返回true，否则返回false</returns>
        /// <remarks>
        /// 检查曲率是否一致的流程：
        /// 1. 确保有足够的点来进行分析（至少15个点）
        /// 2. 计算每个点的曲率（使用前后各两个点）
        /// 3. 过滤掉无效的曲率值（NaN或无穷大），并取绝对值
        /// 4. 确保有足够的有效曲率值（至少5个）
        /// 5. 计算曲率的平均值和标准差
        /// 6. 如果平均曲率大于0.001且标准差与平均值的比例小于0.5，认为曲率一致
        /// </remarks>
        private bool HasConsistentCurvature(Stroke stroke)
        {
            if (stroke.StylusPoints.Count < 15) return false;

            List<double> curvatures = new List<double>();

            // 计算每个点的曲率
            for (int i = 2; i < stroke.StylusPoints.Count - 2; i++)
            {
                Point p1 = stroke.StylusPoints[i - 2].ToPoint();
                Point p2 = stroke.StylusPoints[i].ToPoint();
                Point p3 = stroke.StylusPoints[i + 2].ToPoint();

                double curvature = CalculateCurvature(p1, p2, p3);
                if (!double.IsNaN(curvature) && !double.IsInfinity(curvature))
                {
                    curvatures.Add(Math.Abs(curvature));
                }
            }

            if (curvatures.Count < 5) return false;

            // 计算曲率的标准差
            double avgCurvature = curvatures.Average();
            double variance = curvatures.Select(c => Math.Pow(c - avgCurvature, 2)).Average();
            double stdDev = Math.Sqrt(variance);

            // 如果曲率变化很小且平均曲率不为零，可能是规则曲线
            return avgCurvature > 0.001 && stdDev / avgCurvature < 0.5;
        }

        /// <summary>
        /// 检查圆弧方向是否一致
        /// </summary>
        private bool IsConsistentArcDirection(Stroke stroke)
        {
            if (stroke.StylusPoints.Count < 10) return false;

            Point start = stroke.StylusPoints.First().ToPoint();
            Point end = stroke.StylusPoints.Last().ToPoint();

            int positiveDeviations = 0;
            int negativeDeviations = 0;

            // 检查多个点相对于直线的偏移方向
            for (int i = 1; i < stroke.StylusPoints.Count - 1; i += Math.Max(1, stroke.StylusPoints.Count / 10))
            {
                Point p = stroke.StylusPoints[i].ToPoint();
                double signedDistance = SignedDistanceFromLineToPoint(start, end, p);

                if (Math.Abs(signedDistance) > 5) // 忽略很小的偏移
                {
                    if (signedDistance > 0) positiveDeviations++;
                    else negativeDeviations++;
                }
            }

            // 如果大部分点都在直线的同一侧，说明是一致的弧形
            int totalSignificantDeviations = positiveDeviations + negativeDeviations;
            if (totalSignificantDeviations < 3) return false;

            double consistency = Math.Max(positiveDeviations, negativeDeviations) / (double)totalSignificantDeviations;
            return consistency > 0.8; // 80%的点在同一侧
        }

        /// <summary>
        /// 计算三点的曲率
        /// </summary>
        private double CalculateCurvature(Point p1, Point p2, Point p3)
        {
            // 使用三点计算曲率的公式
            double a = GetDistance(p1, p2);
            double b = GetDistance(p2, p3);
            double c = GetDistance(p1, p3);

            if (a == 0 || b == 0 || c == 0) return 0;

            // 使用海伦公式计算面积
            double s = (a + b + c) / 2;
            double areaSquared = s * (s - a) * (s - b) * (s - c);
            double area = areaSquared > 0 ? Math.Sqrt(areaSquared) : 0;

            // 曲率 = 4 * 面积 / (a * b * c)
            return 4 * area / (a * b * c);
        }

        /// <summary>
        /// 计算点到直线的有符号距离
        /// </summary>
        private double SignedDistanceFromLineToPoint(Point lineStart, Point lineEnd, Point point)
        {
            // 使用叉积计算有符号距离
            double dx = lineEnd.X - lineStart.X;
            double dy = lineEnd.Y - lineStart.Y;
            double lineLength = Math.Sqrt(dx * dx + dy * dy);

            if (lineLength == 0) return 0;

            return ((lineEnd.Y - lineStart.Y) * point.X - (lineEnd.X - lineStart.X) * point.Y +
                    lineEnd.X * lineStart.Y - lineEnd.Y * lineStart.X) / lineLength;
        }

        /// <summary>
        /// 尝试获取直线的端点
        /// </summary>
        /// <param name="stroke">要分析的笔画</param>
        /// <param name="endpoint1">输出参数：直线的第一个端点</param>
        /// <param name="endpoint2">输出参数：直线的第二个端点</param>
        /// <returns>如果成功获取直线端点则返回true，否则返回false</returns>
        /// <remarks>
        /// 尝试获取直线端点的流程：
        /// 1. 确保笔画有足够的点（至少10个点）
        /// 2. 如果启用高精度直线拉直，则对点数进行采样
        /// 3. 使用总最小二乘法(TLS/PCA)进行直线拟合
        /// 4. 计算中心点和协方差矩阵
        /// 5. 计算特征值和特征向量，确定直线方向
        /// 6. 计算解释方差比例（拟合优度）
        /// 7. 计算所有点在直线方向上的投影，找到最小和最大投影值
        /// 8. 根据投影值计算端点坐标
        /// 9. 根据解释方差比例判断是否为直线
        /// </remarks>
        private bool TryGetStraightLineEndpoints(Stroke stroke, out Point endpoint1, out Point endpoint2)
        {
            endpoint1 = new Point();
            endpoint2 = new Point();

            var points = stroke.StylusPoints.Select(p => p.ToPoint()).ToList();
            if (points.Count < 10)
            {
                return false;
            }

            List<Point> workingPoints = points;
            if (Settings.Canvas.HighPrecisionLineStraighten)
            {
                workingPoints = SamplePointsByDistance(points, 10.0);
                Debug.WriteLine($"高精度模式：原始点数={points.Count}, 采样后点数={workingPoints.Count}");
            }

            // 使用总最小二乘法(TLS/PCA)进行直线拟合
            int n = workingPoints.Count - 8;
            if (n < 1)
            {
                // 如果采样后点数太少，回退到原始方法
                n = points.Count - 8;
                workingPoints = points;
            }

            List<Point> filteredPoints = new List<Point>();

            // 收集过滤后的点（跳过前 4 个和后 4 个点，用于计算直线方向）
            int skipCount = Math.Min(4, n / 2); // 确保跳过数量不超过一半
            for (int i = skipCount; i < n + skipCount && i < workingPoints.Count; i++)
            {
                filteredPoints.Add(workingPoints[i]);
            }

            // 计算中心点（使用过滤后的点）
            double centerX = 0, centerY = 0;
            foreach (Point p in filteredPoints)
            {
                centerX += p.X;
                centerY += p.Y;
            }
            centerX /= filteredPoints.Count;
            centerY /= filteredPoints.Count;

            // 计算协方差矩阵（使用过滤后的点）
            double covXX = 0, covYY = 0, covXY = 0;
            foreach (Point p in filteredPoints)
            {
                double dx = p.X - centerX;
                double dy = p.Y - centerY;
                covXX += dx * dx;
                covYY += dy * dy;
                covXY += dx * dy;
            }

            // 计算特征值和特征向量
            double trace = covXX + covYY;
            double determinant = covXX * covYY - covXY * covXY;
            double discriminantSquared = trace * trace - 4 * determinant;
            double discriminant = discriminantSquared > 0 ? Math.Sqrt(discriminantSquared) : 0;

            double eigenvalue1 = (trace + discriminant) / 2;
            double eigenvalue2 = (trace - discriminant) / 2;

            // 最大特征值对应的特征向量即为直线方向
            double directionX, directionY;
            if (Math.Abs(covXY) > 1e-10)
            {
                directionX = covXY;
                directionY = eigenvalue1 - covXX;
                // 归一化
                double length = Math.Sqrt(directionX * directionX + directionY * directionY);
                if (length > 1e-10)
                {
                    directionX /= length;
                    directionY /= length;
                }
                else
                {
                    // 如果归一化失败，使用起点和终点计算方向
                    Point start = points.First();
                    Point end = points.Last();
                    double dx = end.X - start.X;
                    double dy = end.Y - start.Y;
                    double lineLength = Math.Sqrt(dx * dx + dy * dy);
                    if (lineLength > 1e-10)
                    {
                        directionX = dx / lineLength;
                        directionY = dy / lineLength;
                    }
                    else
                    {
                        directionX = (covXX >= covYY) ? 1 : 0;
                        directionY = (covXX >= covYY) ? 0 : 1;
                    }
                }
            }
            else
            {
                Point start = points.First();
                Point end = points.Last();
                double dx = end.X - start.X;
                double dy = end.Y - start.Y;
                double lineLength = Math.Sqrt(dx * dx + dy * dy);

                if (lineLength > 1e-10)
                {
                    directionX = dx / lineLength;
                    directionY = dy / lineLength;
                }
                else
                {
                    if (Math.Abs(eigenvalue1 - covXX) < Math.Abs(eigenvalue1 - covYY))
                    {
                        // 主要方向是 X 轴方向
                        directionX = 1;
                        directionY = 0;
                    }
                    else
                    {
                        // 主要方向是 Y 轴方向
                        directionX = 0;
                        directionY = 1;
                    }
                }
            }

            // 计算解释方差比例（拟合优度）
            double totalVariance = eigenvalue1 + eigenvalue2;
            double explainedVarianceRatio = (totalVariance > 1e-10) ?
                Math.Max(eigenvalue1, eigenvalue2) / totalVariance : 1d;

            // 使用所有点计算端点
            double minProjection = double.MaxValue;
            double maxProjection = double.MinValue;

            // 计算所有点在直线方向上的投影
            List<Point> pointsForProjection = Settings.Canvas.HighPrecisionLineStraighten ? workingPoints : points;
            foreach (Point p in pointsForProjection)
            {
                // 相对于过滤点中心的投影
                double projection = (p.X - centerX) * directionX + (p.Y - centerY) * directionY;
                minProjection = Math.Min(minProjection, projection);
                maxProjection = Math.Max(maxProjection, projection);
            }

            // 计算端点坐标
            endpoint1 = new Point(
                centerX + minProjection * directionX,
                centerY + minProjection * directionY
            );

            endpoint2 = new Point(
                centerX + maxProjection * directionX,
                centerY + maxProjection * directionY
            );

            // 使用解释方差比例作为判断条件
            double threshold = 0.998 + Settings.InkToShape.LineNormalizationThreshold / 500;
            return explainedVarianceRatio > threshold;
        }

        /// <summary>
        /// 确定笔画是否应该被拉成直线
        /// </summary>
        /// <param name="stroke">要分析的笔画</param>
        /// <returns>如果笔画应该被拉成直线则返回true，否则返回false</returns>
        /// <remarks>
        /// 确定笔画是否应该被拉成直线的流程：
        /// 1. 计算线条长度和分辨率自适应阈值
        /// 2. 如果线条太短，不进行拉直处理
        /// 3. 检查线条复杂度，如果是复杂形状，不进行拉直处理
        /// 4. 尝试获取直线端点，判断是否满足直线条件
        /// 5. 根据判断结果返回相应的布尔值
        /// </remarks>
        private bool ShouldStraightenLine(Stroke stroke)
        {
            // 分辨率自适应阈值
            Point start = stroke.StylusPoints.First().ToPoint();
            Point end = stroke.StylusPoints.Last().ToPoint();
            double lineLength = GetDistance(start, end);
            double adaptiveThreshold = Settings.Canvas.AutoStraightenLineThreshold * GetResolutionScale();

            // 如果线条太短，不进行拉直处理
            if (lineLength < adaptiveThreshold)
            {
                Debug.WriteLine($"线条太短: {lineLength} < {adaptiveThreshold}");
                return false;
            }

            // 检查复杂度
            if (IsComplexShape(stroke))
            {
                Debug.WriteLine("拒绝拉直：检测到复杂形状");
                return false;
            }

            Point endpoint1, endpoint2;
            bool shouldStraighten = TryGetStraightLineEndpoints(stroke, out endpoint1, out endpoint2);

            if (shouldStraighten)
            {
                Debug.WriteLine($"接受拉直：判断为直线，解释方差比例满足阈值");
            }
            else
            {
                Debug.WriteLine($"拒绝拉直：判断不满足直线条件");
            }

            return shouldStraighten;
        }

        /// <summary>
        /// 计算墨迹的直线度评分（0-1，1表示完美直线）
        /// </summary>
        /// <param name="stroke">要分析的笔画</param>
        /// <returns>返回直线度评分，范围为0到1，1表示完美直线</returns>
        /// <remarks>
        /// 计算墨迹直线度评分的流程：
        /// 1. 确保笔画有足够的点（至少3个点）
        /// 2. 计算线条长度
        /// 3. 计算偏差评分（基于点到直线的距离）：
        ///    - 计算所有点到直线的平均偏差和最大偏差
        ///    - 根据偏差计算评分
        /// 4. 计算方向一致性评分：
        ///    - 计算每个线段与目标方向的角度差
        ///    - 将角度差转换为评分
        /// 5. 计算路径效率评分：
        ///    - 计算实际路径长度与直线距离的比例
        /// 6. 计算端点连接度评分（默认满分）
        /// 7. 综合评分（加权平均）：
        ///    - 偏差评分：40%
        ///    - 方向一致性评分：30%
        ///    - 路径效率评分：20%
        ///    - 端点连接度评分：10%
        /// 8. 返回最终评分，确保在0到1之间
        /// </remarks>
        private double CalculateStraightnessScore(Stroke stroke)
        {
            if (stroke.StylusPoints.Count < 3) return 0;

            Point start = stroke.StylusPoints.First().ToPoint();
            Point end = stroke.StylusPoints.Last().ToPoint();
            double lineLength = GetDistance(start, end);

            if (lineLength == 0) return 0;

            // 1. 计算偏差评分（基于点到直线的距离）
            double totalDeviation = 0;
            double maxDeviation = 0;
            int pointCount = 0;

            foreach (StylusPoint sp in stroke.StylusPoints)
            {
                Point p = sp.ToPoint();
                double deviation = DistanceFromLineToPoint(start, end, p);
                totalDeviation += deviation;
                maxDeviation = Math.Max(maxDeviation, deviation);
                pointCount++;
            }

            double avgDeviation = totalDeviation / pointCount;

            // 偏差评分：基于平均偏差和最大偏差
            double deviationScore = Math.Max(0, 1 - (avgDeviation / (lineLength * 0.05)) - (maxDeviation / (lineLength * 0.1)));

            // 2. 计算方向一致性评分
            double directionScore = CalculateDirectionConsistency(stroke);

            // 3. 计算路径效率评分（实际路径长度 vs 直线距离）
            double actualLength = 0;
            for (int i = 1; i < stroke.StylusPoints.Count; i++)
            {
                Point p1 = stroke.StylusPoints[i - 1].ToPoint();
                Point p2 = stroke.StylusPoints[i].ToPoint();
                actualLength += GetDistance(p1, p2);
            }
            double efficiencyScore = Math.Max(0, Math.Min(1, lineLength / actualLength));

            // 4. 计算端点连接度评分（起点到终点的直接性）
            double endpointScore = 1.0; // 默认满分，因为我们已经有了起点和终点

            // 综合评分（加权平均）
            double finalScore = (deviationScore * 0.4 + directionScore * 0.3 + efficiencyScore * 0.2 + endpointScore * 0.1);

            Debug.WriteLine($"直线度评分详情: 偏差={deviationScore:F3}, 方向={directionScore:F3}, 效率={efficiencyScore:F3}, 综合={finalScore:F3}");

            return Math.Max(0, Math.Min(1, finalScore));
        }

        /// <summary>
        /// 计算方向一致性评分
        /// </summary>
        /// <param name="stroke">要分析的笔画</param>
        /// <returns>返回方向一致性评分，范围为0到1，1表示方向完全一致</returns>
        /// <remarks>
        /// 计算方向一致性评分的流程：
        /// 1. 确保笔画有足够的点（至少5个点）
        /// 2. 计算目标方向（从起点到终点的方向）
        /// 3. 计算每个线段与目标方向的角度差：
        ///    - 遍历笔画中的每个线段
        ///    - 忽略太短的线段（长度小于2）
        ///    - 计算线段的角度
        ///    - 计算与目标方向的角度差
        ///    - 处理角度跨越问题（超过180度的情况）
        /// 4. 计算平均角度差
        /// 5. 将角度差转换为评分（0-1）：
        ///    - 0度差 = 1分
        ///    - 90度差 = 0分
        /// 6. 返回方向一致性评分
        /// </remarks>
        private double CalculateDirectionConsistency(Stroke stroke)
        {
            if (stroke.StylusPoints.Count < 5) return 1.0;

            Point start = stroke.StylusPoints.First().ToPoint();
            Point end = stroke.StylusPoints.Last().ToPoint();

            // 目标方向
            double targetAngle = Math.Atan2(end.Y - start.Y, end.X - start.X);

            double totalAngleDifference = 0;
            int segmentCount = 0;

            // 计算每个线段与目标方向的角度差
            for (int i = 1; i < stroke.StylusPoints.Count; i++)
            {
                Point p1 = stroke.StylusPoints[i - 1].ToPoint();
                Point p2 = stroke.StylusPoints[i].ToPoint();

                double segmentLength = GetDistance(p1, p2);
                if (segmentLength < 2) continue; // 忽略太短的线段

                double segmentAngle = Math.Atan2(p2.Y - p1.Y, p2.X - p1.X);
                double angleDiff = Math.Abs(segmentAngle - targetAngle);

                // 处理角度跨越问题
                if (angleDiff > Math.PI) angleDiff = 2 * Math.PI - angleDiff;

                totalAngleDifference += angleDiff;
                segmentCount++;
            }

            if (segmentCount == 0) return 1.0;

            double avgAngleDifference = totalAngleDifference / segmentCount;

            // 将角度差转换为评分（0-1）
            // 0度差 = 1分，90度差 = 0分
            double directionScore = Math.Max(0, 1 - (avgAngleDifference / (Math.PI / 2)));

            return directionScore;
        }

        /// <summary>
        /// 在两点之间创建直线笔画
        /// </summary>
        /// <param name="start">直线的起始点</param>
        /// <param name="end">直线的结束点</param>
        /// <returns>返回包含直线点集的StylusPointCollection</returns>
        /// <remarks>
        /// 在两点之间创建直线笔画的流程：
        /// 1. 根据是否启用压感触屏模式决定如何设置压感：
        ///    - 如果未启用压感触屏模式、禁用压感、启用无压感矩形或使用钢笔类型1，则使用均匀粗细（压感值0.5）
        ///    - 否则，创建带有压感变化的直线：
        ///      - 计算中点
        ///      - 从起点到中点：压感从0.4渐变到0.8
        ///      - 从中点到终点：压感从0.8渐变到0.4
        /// 2. 使用GeneratePointsBetween方法生成点集
        /// 3. 返回生成的点集
        /// </remarks>
        private StylusPointCollection CreateStraightLine(Point start, Point end)
        {
            StylusPointCollection points = new StylusPointCollection();

            // 根据是否启用压感触屏模式决定如何设置压感
            // 如果未启用压感触屏模式，则使用均匀粗细
            if (!Settings.Canvas.EnablePressureTouchMode || Settings.Canvas.DisablePressure ||
                Settings.InkToShape.IsInkToShapeNoFakePressureRectangle || penType == 1)
            {
                var linePoints = GeneratePointsBetween(start, end, 0.5f, 0.5f, 8.0);
                foreach (var pt in linePoints)
                {
                    points.Add(pt);
                }
            }
            else
            {
                Point midPoint = new Point((start.X + end.X) / 2, (start.Y + end.Y) / 2);

                var startToMid = GeneratePointsBetween(start, midPoint, 0.4f, 0.8f, 8.0);
                foreach (var pt in startToMid)
                {
                    points.Add(pt);
                }

                var midToEnd = GeneratePointsBetween(midPoint, end, 0.8f, 0.4f, 8.0);
                for (int i = 1; i < midToEnd.Count; i++)
                {
                    points.Add(midToEnd[i]);
                }
            }

            return points;
        }

        /// <summary>
        /// 根据距离对点数进行采样
        /// </summary>
        /// <param name="points">原始点列表</param>
        /// <param name="sampleInterval">采样间隔，默认为10.0</param>
        /// <returns>返回采样后的点列表</returns>
        /// <remarks>
        /// 根据距离对点数进行采样的流程：
        /// 1. 确保原始点列表不为空且至少有2个点
        /// 2. 总是包含起点
        /// 3. 遍历原始点列表，计算累积距离：
        ///    - 当累积距离达到采样间隔时，添加当前点
        ///    - 重置累积距离
        /// 4. 总是包含终点（如果还没有包含）
        /// 5. 返回采样后的点列表
        /// </remarks>
        private List<Point> SamplePointsByDistance(List<Point> points, double sampleInterval = 10.0)
        {
            if (points == null || points.Count < 2)
                return points;

            List<Point> sampledPoints = new List<Point>();
            sampledPoints.Add(points[0]); // 总是包含起点

            double accumulatedDistance = 0;
            Point lastSampledPoint = points[0];

            for (int i = 1; i < points.Count; i++)
            {
                double segmentDistance = GetDistance(lastSampledPoint, points[i]);
                accumulatedDistance += segmentDistance;

                // 当累积距离达到采样间隔时，添加当前点
                if (accumulatedDistance >= sampleInterval)
                {
                    sampledPoints.Add(points[i]);
                    lastSampledPoint = points[i];
                    accumulatedDistance = 0; // 重置累积距离
                }
            }

            // 总是包含终点（如果还没有包含）
            if (sampledPoints.Count == 0 || GetDistance(sampledPoints.Last(), points.Last()) > 1.0)
            {
                sampledPoints.Add(points.Last());
            }

            return sampledPoints;
        }

        /// <summary>
        /// 计算点到直线的距离
        /// </summary>
        /// <param name="lineStart">直线的起始点</param>
        /// <param name="lineEnd">直线的结束点</param>
        /// <param name="point">要计算距离的点</param>
        /// <returns>返回点到直线的距离</returns>
        /// <remarks>
        /// 计算点到直线距离的流程：
        /// 1. 计算直线的长度
        /// 2. 如果直线长度为0（即两个点重合），则返回点到该点的距离
        /// 3. 否则，使用叉积计算点到直线的垂直距离
        /// 4. 返回计算得到的距离
        /// </remarks>
        private double DistanceFromLineToPoint(Point lineStart, Point lineEnd, Point point)
        {
            // Calculate distance from point to line defined by lineStart and lineEnd
            double lineLength = GetDistance(lineStart, lineEnd);
            if (lineLength == 0) return GetDistance(point, lineStart);

            // Calculate the cross product to get the perpendicular distance
            double distance = Math.Abs((lineEnd.Y - lineStart.Y) * point.X -
                                      (lineEnd.X - lineStart.X) * point.Y +
                                      lineEnd.X * lineStart.Y - lineEnd.Y * lineStart.X) / lineLength;
            return distance;
        }

        /// <summary>
        /// 判断一个 stroke 是否是直线（排除虚线和点线）
        /// </summary>
        /// <param name="stroke">要检查的 stroke</param>
        /// <returns>如果是直线返回 true，否则返回 false</returns>
        /// <remarks>
        /// 判断一个 stroke 是否是直线的流程：
        /// 1. 检查 stroke 是否为空或没有点，如果是则返回 false
        /// 2. 检查点的数量：
        ///    - 如果只有1个点，返回 false
        ///    - 如果有2个点：
        ///      - 计算两点之间的距离
        ///      - 如果距离小于10，返回 false
        ///      - 否则返回 true
        ///    - 如果有3个点：
        ///      - 计算第一个点和第三个点之间的距离
        ///      - 如果距离小于10，返回 false
        ///      - 计算第二个点到由第一个点和第三个点组成的直线的距离
        ///      - 如果距离相对于线段长度很小（小于1%），认为是直线，返回 true
        ///      - 否则返回 false
        ///    - 如果点的数量大于3，返回 false
        /// </remarks>
        private bool IsStraightLine(Stroke stroke)
        {
            if (stroke == null || stroke.StylusPoints.Count == 0)
                return false;

            int pointCount = stroke.StylusPoints.Count;

            if (pointCount == 1)
                return false;

            // 最简单的直线：只有2个点
            if (pointCount == 2)
            {
                Point p1 = stroke.StylusPoints[0].ToPoint();
                Point p2 = stroke.StylusPoints[1].ToPoint();
                double lineLength = GetDistance(p1, p2);

                if (lineLength < 10)
                    return false;

                return true;
            }

            if (pointCount > 3)
                return false;

            // 对于3个点的情况，检查它们是否基本在一条直线上
            if (pointCount == 3)
            {
                Point p1 = stroke.StylusPoints[0].ToPoint();
                Point p2 = stroke.StylusPoints[1].ToPoint();
                Point p3 = stroke.StylusPoints[2].ToPoint();

                double totalLength = GetDistance(p1, p3);
                if (totalLength < 10)
                    return false;

                // 计算点到直线的距离
                // 使用 p1 和 p3 作为直线端点，检查 p2 是否在这条直线上
                double distance = DistanceFromLineToPoint(p1, p3, p2);

                // 如果点到直线的距离相对于线段长度很小，认为是直线
                // 使用相对误差阈值（比如 1%）
                if (totalLength > 0 && distance / totalLength < 0.01)
                    return true;

                return false;
            }

            return false;
        }

        private bool IsValidStraightLineSnapTarget(Stroke stroke)
        {
            if (stroke == null || stroke.StylusPoints.Count == 0)
                return false;
            if (stroke.StylusPoints.Count == 1)
                return false;
            if (stroke.StylusPoints.Count <= 3)
            {
                if (!IsStraightLine(stroke))
                    return false;
                double len = GetDistance(stroke.StylusPoints.First().ToPoint(), stroke.StylusPoints.Last().ToPoint());
                if (len < 20)
                    return false;
                return true;
            }
            return IsPotentialStraightLine(stroke);
        }

        /// <summary>
        /// 尝试将直线端点吸附到现有笔画的端点
        /// </summary>
        /// <param name="start">直线的起始点</param>
        /// <param name="end">直线的结束点</param>
        /// <returns>返回吸附后的端点数组，如果没有发生吸附则返回null</returns>
        /// <remarks>
        /// 尝试将直线端点吸附到现有笔画端点的流程：
        /// 1. 检查是否启用了线段端点吸附功能，如果没有启用则返回null
        /// 2. 初始化吸附状态和吸附后的点
        /// 3. 获取设置中的吸附距离阈值
        /// 4. 遍历画布中的所有笔画：
        ///    - 跳过没有点的笔画
        ///    - 只对直线进行端点吸附，跳过虚线和点线
        ///    - 获取笔画的起点和终点
        ///    - 检查起点是否应该吸附到现有笔画的端点
        ///    - 检查终点是否应该吸附到现有笔画的端点
        ///    - 如果两个端点都已经吸附，结束遍历
        /// 5. 如果发生了吸附，返回吸附后的端点数组，否则返回null
        /// </remarks>
        private Point[] GetSnappedEndpoints(Point start, Point end)
        {
            if (!Settings.Canvas.LineEndpointSnapping)
                return null;

            bool startSnapped = false;
            bool endSnapped = false;
            Point snappedStart = start;
            Point snappedEnd = end;

            // 使用设置中的吸附距离阈值
            double snapThreshold = Settings.Canvas.LineEndpointSnappingThreshold;

            // Check all strokes in canvas for potential snap points
            foreach (Stroke stroke in inkCanvas.Strokes)
            {
                if (stroke.StylusPoints.Count == 0) continue;

                // 只对直线进行端点吸附，跳过虚线和点线
                if (!IsValidStraightLineSnapTarget(stroke))
                    continue;

                // Get stroke endpoints
                Point strokeStart = stroke.StylusPoints.First().ToPoint();
                Point strokeEnd = stroke.StylusPoints.Last().ToPoint();

                // Check if start point should snap to an endpoint
                if (!startSnapped)
                {
                    if (GetDistance(start, strokeStart) < snapThreshold)
                    {
                        snappedStart = strokeStart;
                        startSnapped = true;
                    }
                    else if (GetDistance(start, strokeEnd) < snapThreshold)
                    {
                        snappedStart = strokeEnd;
                        startSnapped = true;
                    }
                }

                // Check if end point should snap to an endpoint
                if (!endSnapped)
                {
                    if (GetDistance(end, strokeStart) < snapThreshold)
                    {
                        snappedEnd = strokeStart;
                        endSnapped = true;
                    }
                    else if (GetDistance(end, strokeEnd) < snapThreshold)
                    {
                        snappedEnd = strokeEnd;
                        endSnapped = true;
                    }
                }

                // If both endpoints are snapped, we're done
                if (startSnapped && endSnapped) break;
            }

            // Return snapped points if any snapping occurred
            if (startSnapped || endSnapped)
            {
                return new[] { snappedStart, snappedEnd };
            }

            return null;
        }

        /// <summary>
        /// 设置新的笔画备份
        /// </summary>
        /// <remarks>
        /// 设置新的笔画备份的流程：
        /// 1. 克隆当前墨水画布的笔画集合
        /// 2. 获取当前白板索引
        /// 3. 如果当前模式为0，则将白板索引设置为0
        /// 4. 将克隆的笔画集合存储到strokeCollections中对应索引的位置
        /// </remarks>
        private void SetNewBackupOfStroke()
        {
            lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
        }

        /// <summary>
        /// 计算两点之间的距离
        /// </summary>
        /// <param name="point1">第一个点</param>
        /// <param name="point2">第二个点</param>
        /// <returns>返回两点之间的距离</returns>
        /// <remarks>
        /// 使用欧几里得距离公式计算两点之间的距离：
        /// distance = √[(x2 - x1)² + (y2 - y1)²]
        /// </remarks>
        public double GetDistance(Point point1, Point point2)
        {
            return Math.Sqrt((point1.X - point2.X) * (point1.X - point2.X) +
                             (point1.Y - point2.Y) * (point1.Y - point2.Y));
        }

        /// <summary>
        /// 计算点的速度
        /// </summary>
        /// <param name="point1">第一个点</param>
        /// <param name="point2">第二个点（当前点）</param>
        /// <param name="point3">第三个点</param>
        /// <returns>返回点的速度</returns>
        /// <remarks>
        /// 计算点速度的流程：
        /// 1. 计算第一个点到第二个点的距离
        /// 2. 计算第三个点到第二个点的距离
        /// 3. 将两个距离相加
        /// 4. 除以20，得到速度值
        /// </remarks>
        public double GetPointSpeed(Point point1, Point point2, Point point3)
        {
            return (Math.Sqrt((point1.X - point2.X) * (point1.X - point2.X) +
                              (point1.Y - point2.Y) * (point1.Y - point2.Y))
                    + Math.Sqrt((point3.X - point2.X) * (point3.X - point2.X) +
                                (point3.Y - point2.Y) * (point3.Y - point2.Y)))
                   / 20;
        }

        private static float RateBasedPressureFactorFromPointSpeed(double speed)
        {
            if (speed >= 0.25)
                return (float)(0.5 - 0.3 * (Math.Min(speed, 1.5) - 0.3) / 1.2);
            if (speed >= 0.05)
                return 0.5f;
            return (float)(0.5 + 0.4 * (0.05 - speed) / 0.05);
        }

        private static float RealtimeBrushTipMixRatePressureFromSpeed(double speed)
        {
            if (speed < 0) speed = 0;
            const double slowRef = 0.012;
            const double fastRef = 0.5;
            var t = (speed - slowRef) / (fastRef - slowRef);
            if (t < 0) t = 0;
            else if (t > 1) t = 1;
            t = t * t * (3.0 - 2.0 * t);
            const double pThick = 0.9;
            const double pThin = 0.22;
            var p = pThick + (pThin - pThick) * t;
            return (float)Math.Max(0.08, Math.Min(1.0, p));
        }

        private static bool IsStrokePressureApproximatelyConstant(StylusPointCollection pts, out float meanPf)
        {
            meanPf = 0.5f;
            if (pts == null || pts.Count == 0) return true;
            double sum = 0;
            foreach (StylusPoint p in pts) sum += p.PressureFactor;
            meanPf = (float)(sum / pts.Count);
            const float tol = 0.04f;
            foreach (StylusPoint p in pts)
            {
                if (Math.Abs(p.PressureFactor - meanPf) > tol)
                    return false;
            }

            return true;
        }

        private void ReplaceStrokesSafely(StrokeCollection strokesToRemove, Stroke replacementStroke, Stroke fallbackOriginalStroke = null)
        {
            if (replacementStroke == null) return;
            try
            {
                SetNewBackupOfStroke();
                _currentCommitType = CommitReason.ShapeRecognition;

                if (strokesToRemove != null && strokesToRemove.Count > 0)
                    inkCanvas.Strokes.Remove(strokesToRemove);
                if (fallbackOriginalStroke != null && inkCanvas.Strokes.Contains(fallbackOriginalStroke))
                    inkCanvas.Strokes.Remove(fallbackOriginalStroke);

                if (!inkCanvas.Strokes.Contains(replacementStroke))
                    inkCanvas.Strokes.Add(replacementStroke);
            }
            finally
            {
                _currentCommitType = CommitReason.UserInput;
            }
        }

        private void ReplaceStrokesSafely(StrokeCollection strokesToRemove, StrokeCollection replacementStrokes, Stroke fallbackOriginalStroke = null)
        {
            if (replacementStrokes == null || replacementStrokes.Count == 0) return;
            try
            {
                SetNewBackupOfStroke();
                _currentCommitType = CommitReason.ShapeRecognition;

                if (strokesToRemove != null && strokesToRemove.Count > 0)
                    inkCanvas.Strokes.Remove(strokesToRemove);
                if (fallbackOriginalStroke != null && inkCanvas.Strokes.Contains(fallbackOriginalStroke))
                    inkCanvas.Strokes.Remove(fallbackOriginalStroke);

                inkCanvas.Strokes.Add(replacementStrokes);
            }
            finally
            {
                _currentCommitType = CommitReason.UserInput;
            }
        }

        /// <summary>
        /// 将沿线速度映射为压感并与硬件压感混合，快写略细、慢写略粗；在落笔时（及手写笔移动时由调用方）统一施加。
        /// 无压感设备上系统可能将 <see cref="DrawingAttributes.IgnorePressure"/> 置为 true，此处强制关闭以便粗细随合成压感变化。
        /// 若 <see cref="Settings.Canvas.DisablePressure"/> 为 true，本方法直接返回且不修改 IgnorePressure。
        /// </summary>
        private void ApplyVelocityBrushTipFromSpeed(Stroke stroke)
        {
            try
            {
                if (Settings.Canvas.DisablePressure)
                    return;

                var mix = Settings.Canvas.VelocityBrushTipMix;
                if (mix <= 0 || stroke == null) return;
                if (mix > 1) mix = 1;

                if (stroke.DrawingAttributes != null)
                    stroke.DrawingAttributes.IgnorePressure = false;

                var pts = stroke.StylusPoints;
                if (pts.Count < 3) return;

                var effectiveMix = (float)mix;
                if (IsStrokePressureApproximatelyConstant(pts, out _))
                    effectiveMix = Math.Max(effectiveMix, 0.78f);

                var n = pts.Count - 1;
                var stylusPoints = new StylusPointCollection();

                for (var i = 0; i <= n; i++)
                {
                    var speed = GetPointSpeed(
                        pts[Math.Max(i - 1, 0)].ToPoint(),
                        pts[i].ToPoint(),
                        pts[Math.Min(i + 1, n)].ToPoint());

                    var speedPressure = RealtimeBrushTipMixRatePressureFromSpeed(speed);

                    var basePf = pts[i].PressureFactor;
                    var blended = (1.0f - effectiveMix) * basePf + effectiveMix * speedPressure;
                    blended = (float)Math.Max(0.08, Math.Min(1.0, blended));

                    var p = new StylusPoint(pts[i].X, pts[i].Y, blended);
                    stylusPoints.Add(p);
                }

                stroke.StylusPoints = stylusPoints;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
        }

        public Point[] FixPointsDirection(Point p1, Point p2)
        {
            double deltaY = Math.Abs(p1.Y - p2.Y);
            double deltaX = Math.Abs(p1.X - p2.X);

            if (deltaY < 1e-10 || deltaX / deltaY > 8)
            {
                //水平
                var x = deltaY / 2;
                if (p1.Y > p2.Y)
                {
                    p1.Y -= x;
                    p2.Y += x;
                }
                else
                {
                    p1.Y += x;
                    p2.Y -= x;
                }
            }
            else if (deltaX < 1e-10 || deltaY / deltaX > 8)
            {
                //垂直
                var x = deltaX / 2;
                if (p1.X > p2.X)
                {
                    p1.X -= x;
                    p2.X += x;
                }
                else
                {
                    p1.X += x;
                    p2.X -= x;
                }
            }

            return new Point[2] { p1, p2 };
        }

        /// <summary>
        /// 用点集拟合出 IACore 风格椭圆参数（中心/长短半轴/方向/四个端点）。
        /// 解决 WinRT 返回热点顺序不稳定导致椭圆纠正角度翻转的问题。
        /// </summary>
        private static bool TryEstimateEllipseParamsFromStrokes(
            StrokeCollection strokes,
            out Point centroid,
            out double a,
            out double b,
            out double thetaRad,
            out Point major0,
            out Point major1,
            out Point minor0,
            out Point minor1)
        {
            centroid = default;
            a = b = 0;
            thetaRad = 0;
            major0 = major1 = minor0 = minor1 = default;

            if (strokes == null || strokes.Count == 0) return false;

            var pts = new List<Point>(256);
            foreach (var s in strokes)
            {
                if (s?.StylusPoints == null) continue;
                foreach (var sp in s.StylusPoints)
                    pts.Add(sp.ToPoint());
            }

            if (pts.Count < 12) return false;

            double mx = 0, my = 0;
            for (int i = 0; i < pts.Count; i++)
            {
                mx += pts[i].X;
                my += pts[i].Y;
            }
            mx /= pts.Count;
            my /= pts.Count;
            centroid = new Point(mx, my);

            double sxx = 0, syy = 0, sxy = 0;
            for (int i = 0; i < pts.Count; i++)
            {
                var dx = pts[i].X - mx;
                var dy = pts[i].Y - my;
                sxx += dx * dx;
                syy += dy * dy;
                sxy += dx * dy;
            }

            if (sxx + syy < 1e-6) return false;

            thetaRad = 0.5 * Math.Atan2(2.0 * sxy, sxx - syy);
            if (double.IsNaN(thetaRad) || double.IsInfinity(thetaRad)) return false;

            // 主轴单位向量 v1=(cos,sin)，次轴 v2=(-sin,cos)
            var cos = Math.Cos(thetaRad);
            var sin = Math.Sin(thetaRad);

            // 投影收集，用分位数抑制离群点
            var us = new double[pts.Count];
            var vs = new double[pts.Count];
            double maxU = double.MinValue, minU = double.MaxValue;
            double maxV = double.MinValue, minV = double.MaxValue;
            for (int i = 0; i < pts.Count; i++)
            {
                var dx = pts[i].X - mx;
                var dy = pts[i].Y - my;
                var u = dx * cos + dy * sin;
                var v = -dx * sin + dy * cos;
                us[i] = u;
                vs[i] = v;
                if (u > maxU) maxU = u;
                if (u < minU) minU = u;
                if (v > maxV) maxV = v;
                if (v < minV) minV = v;
            }

            Array.Sort(us);
            Array.Sort(vs);

            int hi = (int)Math.Round((pts.Count - 1) * 0.98);
            int lo = (int)Math.Round((pts.Count - 1) * 0.02);
            hi = Math.Max(0, Math.Min(pts.Count - 1, hi));
            lo = Math.Max(0, Math.Min(pts.Count - 1, lo));

            var uHi = us[hi];
            var uLo = us[lo];
            var vHi = vs[hi];
            var vLo = vs[lo];

            var aCandidate = Math.Max(Math.Abs(uHi), Math.Abs(uLo));
            var bCandidate = Math.Max(Math.Abs(vHi), Math.Abs(vLo));
            if (aCandidate < 1e-3) aCandidate = Math.Max(Math.Abs(maxU), Math.Abs(minU));
            if (bCandidate < 1e-3) bCandidate = Math.Max(Math.Abs(maxV), Math.Abs(minV));

            a = aCandidate;
            b = bCandidate;

            // 保证 a 为长半轴
            if (b > a)
            {
                var t = a; a = b; b = t;
                thetaRad += Math.PI / 2;
                cos = Math.Cos(thetaRad);
                sin = Math.Sin(thetaRad);
            }

            major0 = new Point(mx - a * cos, my - a * sin);
            major1 = new Point(mx + a * cos, my + a * sin);
            minor0 = new Point(mx + b * sin, my - b * cos);
            minor1 = new Point(mx - b * sin, my + b * cos);

            return a > 1e-2 && b > 1e-2;
        }

        /// <summary>
        /// 形状纠正拟合度门禁：把 WinRT/IACore 识别出的理想形状与原始笔画对比，
        /// 吻合度过低则返回 false（消费端 return，保留原笔画而不强行纠正）。
        /// 任一度量异常或数据不足时 fail-open 返回 true，避免度量 bug 静默关掉整个形状纠正。
        /// 阈值固定为默认严格度（等价 strictness=0.5）：多边形归一化残差 ≤0.20，
        /// 椭圆代数残差 mean|q| ≤0.25 且闭合度 ≤0.25。
        /// </summary>
        private bool PassesShapeFitGate(InkShapeRecognitionResult result)
        {
            if (result == null || !result.IsSuccess || result.StrokesToRemove == null || result.StrokesToRemove.Count == 0)
                return true; // 上游已 IsSuccess 判过；到这里说明可放行

            try
            {
                // 坐标防御：画布在视频展台等场景被反复 ScaleAt 缩放后，WinRT/IACore 返回的
                // Centroid/HotPoints/ShapeWidth/Height 可能非有限或巨量。这类结果直接拒绝，
                // 保留原笔画，避免后续 GeneratePointsBetween/GenerateEllipseGeometry 生成海量/NaN 点导致 OOM 卡死。
                if (!double.IsFinite(result.ShapeWidth) || !double.IsFinite(result.ShapeHeight) ||
                    result.ShapeWidth <= 0 || result.ShapeHeight <= 0 ||
                    !double.IsFinite(result.Centroid.X) || !double.IsFinite(result.Centroid.Y))
                    return false;

                // 形状比画布大得离谱（>8 倍画布尺寸）说明坐标系已被极端缩放，纠正无意义且易 OOM。
                var canvasW = inkCanvas?.ActualWidth ?? 0;
                var canvasH = inkCanvas?.ActualHeight ?? 0;
                if (canvasW > 0 && canvasH > 0)
                {
                    if (result.ShapeWidth > canvasW * 8 || result.ShapeHeight > canvasH * 8)
                        return false;
                }

                if (result.HotPoints != null)
                {
                    for (int i = 0; i < result.HotPoints.Count; i++)
                    {
                        if (!double.IsFinite(result.HotPoints[i].X) || !double.IsFinite(result.HotPoints[i].Y))
                            return false;
                    }
                }

                var name = result.ShapeName ?? string.Empty;

                // 椭圆/圆：用 PCA 主轴系算代数残差 + 闭合度
                if (name == "Circle" || name == "Ellipse")
                {
                    if (!TryEstimateEllipseParamsFromStrokes(
                            result.StrokesToRemove,
                            out var centroid, out var a, out var b, out var thetaRad,
                            out _, out _, out _, out _))
                        return true; // 反推失败→不拦，交给既有逻辑

                    var residual = MeasureEllipseAlgebraicResidual(result.StrokesToRemove, centroid, a, b, thetaRad);
                    var openness = MeasureOpennessRatio(result.StrokesToRemove);
                    return residual <= 0.25 && openness <= 0.25;
                }

                // 三角形/矩形族：多边形边距残差（闭合边残差过大时 isClosed=false，自然被拒）
                if (name == "Triangle" || name.Contains("Rectangle") || name.Contains("Diamond") ||
                    name.Contains("Parallelogram") || name.Contains("Square") ||
                    name.Contains("Trapezoid") || name.Contains("Quadrilateral"))
                {
                    if (result.HotPoints == null || result.HotPoints.Count < 3)
                        return true;
                    var residual = MeasurePolygonFitResidual(result.StrokesToRemove, result.HotPoints, out _);
                    return residual <= 0.20;
                }

                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"形状拟合度门禁异常(fail-open): {ex.Message}");
                return true;
            }
        }

        /// <summary>
        /// 多边形拟合残差：采样原始笔画点，取到理想多边形各边(含闭合边)的最近距离均值，
        /// 用对角线 sqrt(w²+h²) 归一化。0=完美。闭合边残差过大时 isClosed=false。
        /// </summary>
        private double MeasurePolygonFitResidual(StrokeCollection originalStrokes, PointCollection hotPoints, out bool isClosed)
        {
            isClosed = true;
            if (originalStrokes == null || originalStrokes.Count == 0 || hotPoints == null || hotPoints.Count < 3)
                return 0.0;

            // 收集并采样原始笔画点
            var raw = new List<Point>(256);
            foreach (var s in originalStrokes)
            {
                if (s?.StylusPoints == null) continue;
                foreach (var sp in s.StylusPoints) raw.Add(sp.ToPoint());
            }
            if (raw.Count < 2) return 0.0;
            var pts = SamplePointsByDistance(raw, 10.0);
            if (pts.Count == 0) return 0.0;

            // 归一化用对角线（用热点包围盒，比 ShapeWidth/Height 更贴合理想多边形）
            double minX = double.MaxValue, maxX = double.MinValue, minY = double.MaxValue, maxY = double.MinValue;
            for (int i = 0; i < hotPoints.Count; i++)
            {
                minX = Math.Min(minX, hotPoints[i].X); maxX = Math.Max(maxX, hotPoints[i].X);
                minY = Math.Min(minY, hotPoints[i].Y); maxY = Math.Max(maxY, hotPoints[i].Y);
            }
            double diag = Math.Sqrt((maxX - minX) * (maxX - minX) + (maxY - minY) * (maxY - minY));
            if (diag < 1e-3) return 1.0;

            int n = hotPoints.Count;
            double sum = 0, closingSum = 0;
            for (int pi = 0; pi < pts.Count; pi++)
            {
                double best = double.MaxValue;
                for (int i = 0; i < n; i++)
                {
                    var a = hotPoints[i];
                    var b2 = hotPoints[(i + 1) % n];
                    double d = DistanceFromLineToPoint(a, b2, pts[pi]);
                    if (d < best) best = d;
                    if (i == n - 1) closingSum += d; // 闭合边 (last→0)
                }
                sum += best;
            }

            double residual = (sum / pts.Count) / diag;
            // 闭合边平均残差若超过对角线一截，视为开放折线被误判为闭合多边形
            double closingAvg = (closingSum / pts.Count) / diag;
            if (closingAvg > 0.5) isClosed = false;
            return residual;
        }

        /// <summary>
        /// 椭圆代数残差：在 PCA 主轴系 (u,v) 下 q=(u/a)²+(v/b)²−1，返回 mean|q|。0=完美椭圆。
        /// </summary>
        private double MeasureEllipseAlgebraicResidual(StrokeCollection originalStrokes, Point centroid, double a, double b, double thetaRad)
        {
            if (originalStrokes == null || originalStrokes.Count == 0 || a < 1e-3 || b < 1e-3)
                return 0.0;

            var raw = new List<Point>(256);
            foreach (var s in originalStrokes)
            {
                if (s?.StylusPoints == null) continue;
                foreach (var sp in s.StylusPoints) raw.Add(sp.ToPoint());
            }
            if (raw.Count < 2) return 0.0;
            var pts = SamplePointsByDistance(raw, 10.0);
            if (pts.Count == 0) return 0.0;

            var cos = Math.Cos(-thetaRad);
            var sin = Math.Sin(-thetaRad);
            double sum = 0;
            for (int i = 0; i < pts.Count; i++)
            {
                double dx = pts[i].X - centroid.X;
                double dy = pts[i].Y - centroid.Y;
                double u = dx * cos - dy * sin;
                double v = dx * sin + dy * cos;
                double q = (u / a) * (u / a) + (v / b) * (v / b) - 1.0;
                sum += Math.Abs(q);
            }
            return sum / pts.Count;
        }

        /// <summary>
        /// 闭合度：原始笔画(按收集顺序)首→末距离 / 路径周长。越大越开放。
        /// </summary>
        private double MeasureOpennessRatio(StrokeCollection originalStrokes)
        {
            if (originalStrokes == null || originalStrokes.Count == 0)
                return 0.0;

            var raw = new List<Point>(256);
            foreach (var s in originalStrokes)
            {
                if (s?.StylusPoints == null) continue;
                foreach (var sp in s.StylusPoints) raw.Add(sp.ToPoint());
            }
            if (raw.Count < 2) return 0.0;

            double perimeter = 0;
            for (int i = 1; i < raw.Count; i++)
                perimeter += GetDistance(raw[i - 1], raw[i]);
            if (perimeter < 1e-3) return 0.0;

            return GetDistance(raw[0], raw[raw.Count - 1]) / perimeter;
        }

        public StylusPointCollection GenerateFakePressureTriangle(StylusPointCollection points)
        {
            var newPoint = new StylusPointCollection();

            if (Settings.InkToShape.IsInkToShapeNoFakePressureTriangle || penType == 1)
            {
                if (points.Count >= 3)
                {
                    for (int i = 0; i < 3; i++)
                    {
                        Point start = points[i].ToPoint();
                        Point end = points[(i + 1) % 3].ToPoint();
                        var edgePoints = GeneratePointsBetween(start, end, 0.5f, 0.5f, 8.0);
                        if (i == 0)
                        {
                            foreach (var pt in edgePoints)
                            {
                                newPoint.Add(pt);
                            }
                        }
                        else
                        {
                            for (int j = 1; j < edgePoints.Count; j++)
                            {
                                newPoint.Add(edgePoints[j]);
                            }
                        }
                    }
                    Point lastPoint = points[0].ToPoint();
                    Point firstPoint = newPoint[0].ToPoint();
                    if (GetDistance(lastPoint, firstPoint) > 1.0)
                    {
                        newPoint.Add(new StylusPoint(lastPoint.X, lastPoint.Y, 0.5f));
                    }
                }
                else
                {
                    return points;
                }
                return newPoint;
            }
            else
            {
                if (points.Count >= 3)
                {
                    for (int i = 0; i < 3; i++)
                    {
                        Point start = points[i].ToPoint();
                        Point end = points[(i + 1) % 3].ToPoint();

                        Point midPoint = GetCenterPoint(start, end);

                        var startToMid = GeneratePointsBetween(start, midPoint, 0.4f, 0.8f, 8.0);
                        if (i == 0)
                        {
                            foreach (var pt in startToMid)
                            {
                                newPoint.Add(pt);
                            }
                        }
                        else
                        {
                            for (int j = 1; j < startToMid.Count; j++)
                            {
                                newPoint.Add(startToMid[j]);
                            }
                        }

                        var midToEnd = GeneratePointsBetween(midPoint, end, 0.8f, 0.4f, 8.0);
                        for (int j = 1; j < midToEnd.Count; j++)
                        {
                            newPoint.Add(midToEnd[j]);
                        }
                    }
                    Point lastPoint = points[0].ToPoint();
                    Point firstPoint = newPoint[0].ToPoint();
                    if (GetDistance(lastPoint, firstPoint) > 1.0)
                    {
                        newPoint.Add(new StylusPoint(lastPoint.X, lastPoint.Y, 0.4f));
                    }
                }
                else
                {
                    return points;
                }
                return newPoint;
            }
        }

        /// <summary>
        /// 在两点之间生成多个点，用于增加图形边缘的点密度
        /// </summary>
        private StylusPointCollection GeneratePointsBetween(Point start, Point end, float startPressure, float endPressure, double minPointInterval = 8.0)
        {
            var result = new StylusPointCollection();

            // 防御：坐标非有限（画布被缩放/平移到极端时识别出的形状坐标可能爆掉）直接退化成两点，
            // 否则下方 pointCount = distance/interval 会爆炸式分配点导致 OOM 卡死。
            if (!double.IsFinite(start.X) || !double.IsFinite(start.Y) ||
                !double.IsFinite(end.X) || !double.IsFinite(end.Y))
            {
                result.Add(new StylusPoint(
                    double.IsFinite(start.X) ? start.X : 0,
                    double.IsFinite(start.Y) ? start.Y : 0, startPressure));
                result.Add(new StylusPoint(
                    double.IsFinite(end.X) ? end.X : 0,
                    double.IsFinite(end.Y) ? end.Y : 0, endPressure));
                return result;
            }

            double distance = GetDistance(start, end);

            if (distance < minPointInterval)
            {
                result.Add(new StylusPoint(start.X, start.Y, startPressure));
                result.Add(new StylusPoint(end.X, end.Y, endPressure));
                return result;
            }

            // 防御：画布在视频展台等场景被反复 ScaleAt 缩放后，笔画坐标空间可能远超屏幕，
            // 形状边长随之放大，未限制时 pointCount 可达数万~数百万 → OOM。
            // 单条边最多 4096 个采样点（8px 间隔下覆盖约 32768px，足够任何可视形状），
            // 超出则按比例拉大采样间隔，渲染结果对肉眼无差别但内存可控。
            const int MaxPointsPerEdge = 4096;
            int pointCount = Math.Max(2, (int)(distance / minPointInterval) + 1);
            if (pointCount > MaxPointsPerEdge)
                pointCount = MaxPointsPerEdge;

            result.Add(new StylusPoint(start.X, start.Y, startPressure));

            for (int i = 1; i < pointCount - 1; i++)
            {
                double ratio = (double)i / (pointCount - 1);
                double pressure = startPressure + (endPressure - startPressure) * ratio;
                double x = start.X + (end.X - start.X) * ratio;
                double y = start.Y + (end.Y - start.Y) * ratio;
                result.Add(new StylusPoint(x, y, (float)pressure));
            }

            result.Add(new StylusPoint(end.X, end.Y, endPressure));

            return result;
        }

        public StylusPointCollection GenerateFakePressureRectangle(StylusPointCollection points)
        {
            var newPoint = new StylusPointCollection();

            if (Settings.InkToShape.IsInkToShapeNoFakePressureRectangle || penType == 1)
            {
                if (points.Count >= 4)
                {
                    for (int i = 0; i < 4; i++)
                    {
                        Point start = points[i].ToPoint();
                        Point end = points[(i + 1) % 4].ToPoint();
                        var edgePoints = GeneratePointsBetween(start, end, 0.5f, 0.5f, 8.0);
                        if (i == 0)
                        {
                            foreach (var pt in edgePoints)
                            {
                                newPoint.Add(pt);
                            }
                        }
                        else
                        {
                            for (int j = 1; j < edgePoints.Count; j++)
                            {
                                newPoint.Add(edgePoints[j]);
                            }
                        }
                    }
                }
                else
                {
                    return points;
                }
                return newPoint;
            }

            if (points.Count >= 4)
            {
                for (int i = 0; i < 4; i++)
                {
                    Point start = points[i].ToPoint();
                    Point end = points[(i + 1) % 4].ToPoint();

                    Point midPoint = GetCenterPoint(start, end);

                    var startToMid = GeneratePointsBetween(start, midPoint, 0.4f, 0.8f, 8.0);
                    if (i == 0)
                    {
                        foreach (var pt in startToMid)
                        {
                            newPoint.Add(pt);
                        }
                    }
                    else
                    {
                        for (int j = 1; j < startToMid.Count; j++)
                        {
                            newPoint.Add(startToMid[j]);
                        }
                    }

                    var midToEnd = GeneratePointsBetween(midPoint, end, 0.8f, 0.4f, 8.0);
                    for (int j = 1; j < midToEnd.Count; j++)
                    {
                        newPoint.Add(midToEnd[j]);
                    }
                }
            }
            else
            {
                return points;
            }

            return newPoint;
        }

        public Point GetCenterPoint(Point point1, Point point2)
        {
            return new Point((point1.X + point2.X) / 2, (point1.Y + point2.Y) / 2);
        }

        public StylusPoint GetCenterPoint(StylusPoint point1, StylusPoint point2)
        {
            return new StylusPoint((point1.X + point2.X) / 2, (point1.Y + point2.Y) / 2);
        }

        // 分辨率自适应：以1080P为基准，返回当前分辨率下的阈值倍数
        private double GetResolutionScale()
        {
            // 以1920x1080为基准
            double baseWidth = 1920.0;
            double baseHeight = 1080.0;
            double screenWidth = SystemParameters.PrimaryScreenWidth;
            double screenHeight = SystemParameters.PrimaryScreenHeight;
            // 取宽高平均缩放，防止极端比例
            double scaleW = screenWidth / baseWidth;
            double scaleH = screenHeight / baseHeight;
            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()
                };
                rectangleStroke.AddPropertyData(Helpers.ModernInkAnalyzer.ShapeStrokePropertyGuid, true);

                // 移除原有的四条直线
                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();
        }

        /// <summary>
        /// 异步墨迹平滑将画布上的 <paramref name="fromStroke"/> 替换为 <paramref name="toStroke"/> 后，把手写字形替换批次里的画布引用一并迁移，使识别仍命中「原始点快照」字典项。
        /// </summary>
        private void MigrateHandwritingBeautifyCanvasStrokeReference(Stroke fromStroke, Stroke toStroke)
        {
            if (fromStroke == null || toStroke == null || ReferenceEquals(fromStroke, toStroke))
                return;
            if (!Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
                return;

            if (_handwritingBeautifyInkInputByCanvasStroke.TryGetValue(fromStroke, out var inkInput))
            {
                _handwritingBeautifyInkInputByCanvasStroke.Remove(fromStroke);
                _handwritingBeautifyInkInputByCanvasStroke[toStroke] = inkInput;
            }

            for (var i = 0; i < _handwritingRecentStrokesForBeautify.Count; i++)
            {
                if (ReferenceEquals(_handwritingRecentStrokesForBeautify[i], fromStroke))
                    _handwritingRecentStrokesForBeautify[i] = toStroke;
            }
        }

        /// <summary>
        /// 收笔后：在墨迹转形状（若启用）完成之后，将笔画并入批次并启动/重置停笔防抖计时器，再于延迟后多笔合并矫正。
        /// </summary>
        private void PruneHandwritingBeautifyBatch()
        {
            for (var i = _handwritingRecentStrokesForBeautify.Count - 1; i >= 0; i--)
            {
                var s = _handwritingRecentStrokesForBeautify[i];
                if (!inkCanvas.Strokes.Contains(s))
                {
                    _handwritingBeautifyInkInputByCanvasStroke.Remove(s);
                    _handwritingRecentStrokesForBeautify.RemoveAt(i);
                }
            }
        }

        private void EnsureHandwritingBeautifyDebounceTimer()
        {
            if (_handwritingBeautifyDebounceTimer != null)
                return;
            _handwritingBeautifyDebounceTimer = new DispatcherTimer(DispatcherPriority.Background, Dispatcher)
            {
                Interval = TimeSpan.FromMilliseconds(CurrentHandwritingBeautifyDebounceMs)
            };
            _handwritingBeautifyDebounceTimer.Tick += HandwritingBeautifyDebounceTimer_Tick;
        }

        /// <summary>
        /// 把当前画布笔画复制为不可变的手写识别输入。识别快照只保留几何与压力，避免 WinRT await 期间画布 Stroke 被修改。
        /// </summary>
        private static Stroke CloneStrokeForHandwritingInput(Stroke source)
        {
            if (source?.StylusPoints == null || source.StylusPoints.Count == 0)
                return null;

            var points = CloneStylusPointCollectionForHandwritingInput(source.StylusPoints);
            if (points == null || points.Count == 0)
                return null;

            return new Stroke(points)
            {
                DrawingAttributes = source.DrawingAttributes?.Clone() ?? new DrawingAttributes()
            };
        }

        /// <summary>平滑替换后刷新对应识别快照，使异步平滑路径与同步路径使用相同的最终几何。</summary>
        private void UpdateHandwritingBeautifyRecognitionSnapshot(Stroke canvasStroke)
        {
            if (canvasStroke == null || !Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
                return;

            var snapshot = CloneStrokeForHandwritingInput(canvasStroke);
            if (snapshot != null)
                _handwritingBeautifyInkInputByCanvasStroke[canvasStroke] = snapshot;
            else
                _handwritingBeautifyInkInputByCanvasStroke.Remove(canvasStroke);
        }

        /// <summary>深拷贝点集，供手写纠正识别输入。</summary>
        private static StylusPointCollection CloneStylusPointCollectionForHandwritingInput(StylusPointCollection source)
        {
            if (source == null || source.Count == 0)
                return null;
            var copy = new StylusPointCollection();
            foreach (StylusPoint p in source)
                copy.Add(new StylusPoint(p.X, p.Y, p.PressureFactor));
            return copy;
        }

        /// <summary>并入批次并重置停笔防抖计时器。识别输入始终从最终画布几何复制。</summary>
        private void ScheduleHandwritingGlyphReplaceAfterStrokeCollected(Stroke strokeForBeautify)
        {
            if (!Settings.InkToShape.EnableWinRtHandwritingStrokeBeautify)
                return;
            if (strokeForBeautify == null || strokeForBeautify.DrawingAttributes?.IsHighlighter == true)
                return;
            if (!inkCanvas.Strokes.Contains(strokeForBeautify))
                return;

            _handwritingBeautifyScheduleRevision++;
            UpdateHandwritingBeautifyRecognitionSnapshot(strokeForBeautify);

            var alreadyInBatch = false;
            foreach (Stroke x in _handwritingRecentStrokesForBeautify)
            {
                if (ReferenceEquals(x, strokeForBeautify))
                {
                    alreadyInBatch = true;
                    break;
                }
            }

            if (!alreadyInBatch)
                _handwritingRecentStrokesForBeautify.Add(strokeForBeautify);

            PruneHandwritingBeautifyBatch();
            while (_handwritingRecentStrokesForBeautify.Count > HandwritingBeautifyMaxRecentStrokes)
            {
                var evicted = _handwritingRecentStrokesForBeautify[0];
                _handwritingRecentStrokesForBeautify.RemoveAt(0);
                _handwritingBeautifyInkInputByCanvasStroke.Remove(evicted);
            }

            EnsureHandwritingBeautifyDebounceTimer();
            _handwritingBeautifyDebounceTimer.Stop();
            _handwritingBeautifyDebounceTimer.Interval = TimeSpan.FromMilliseconds(CurrentHandwritingBeautifyDebounceMs);
            _handwritingBeautifyDebounceTimer.Start();
        }

        private async void HandwritingBeautifyDebounceTimer_Tick(object sender, EventArgs e)
        {
            if (_handwritingBeautifyDebounceTimer != null)
                _handwritingBeautifyDebounceTimer.Stop();

            var revisionWhenIdle = _handwritingBeautifyScheduleRevision;
            try
            {
                await HandwritingGlyphReplaceFromBatchCoreAsync(revisionWhenIdle).ConfigureAwait(true);
            }
            catch (Exception ex)
            {
                LogHelper.WriteLogToFile("[手写体] 停笔防抖矫正异常: " + ex.Message, LogHelper.LogType.Warning);
                Debug.WriteLine(ex);
            }
        }

        private async Task HandwritingGlyphReplaceFromBatchCoreAsync(ulong revisionWhenIdle)
        {
            await InkToShapeSerial.WaitAsync().ConfigureAwait(true);
            try
            {
                if (_handwritingBeautifyScheduleRevision != revisionWhenIdle)
                    return;

                PruneHandwritingBeautifyBatch();

                var canvasStrokes = new StrokeCollection();
                var recognitionInput = new StrokeCollection();
                foreach (Stroke s in _handwritingRecentStrokesForBeautify)
                {
                    if (!inkCanvas.Strokes.Contains(s))
                        continue;
                    canvasStrokes.Add(s);
                    if (_handwritingBeautifyInkInputByCanvasStroke.TryGetValue(s, out var inkInput) && inkInput != null)
                    {
                        recognitionInput.Add(inkInput);
                    }
                    else
                    {
                        LogHelper.WriteLogToFile(
                            "[手写体] 批次识别输入回退为当前画布笔画（未命中最终几何快照）。画布点数=" +
                            (s.StylusPoints?.Count ?? 0),
                            LogHelper.LogType.Info);
                        recognitionInput.Add(s);
                    }
                }

                if (canvasStrokes.Count == 0)
                    return;
                if (_handwritingBeautifyScheduleRevision != revisionWhenIdle)
                    return;

                var shapeMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine);
                var result = await InkRecognizeHelper.CorrectHandwritingStrokesUnifiedAsync(recognitionInput, shapeMode).ConfigureAwait(true);

                if (_handwritingBeautifyScheduleRevision != revisionWhenIdle)
                    return;
                if (result == null || result.Count == 0)
                    return;
                if (ReferenceEquals(result, recognitionInput))
                    return;

                var anyInputStillPresent = false;
                foreach (Stroke s in canvasStrokes)
                {
                    if (inkCanvas.Strokes.Contains(s))
                    {
                        anyInputStillPresent = true;
                        break;
                    }
                }

                if (!anyInputStillPresent)
                    return;

                SetNewBackupOfStroke();
                _currentCommitType = CommitReason.ShapeRecognition;
                foreach (Stroke s in canvasStrokes)
                {
                    if (inkCanvas.Strokes.Contains(s))
                        inkCanvas.Strokes.Remove(s);
                }

                foreach (Stroke s in result)
                    inkCanvas.Strokes.Add(s);
                _currentCommitType = CommitReason.UserInput;

                foreach (Stroke s in canvasStrokes)
                {
                    _handwritingRecentStrokesForBeautify.Remove(s);
                    _handwritingBeautifyInkInputByCanvasStroke.Remove(s);
                }

                PruneHandwritingBeautifyBatch();
            }
            catch (Exception ex)
            {
                LogHelper.WriteLogToFile("[手写体] 收笔替换异常: " + ex.Message, LogHelper.LogType.Warning);
            }
            finally
            {
                InkToShapeSerial.Release();
            }
        }

        #endregion
    }
}