From 9d0baa07990269a833bb8bd91d3293183148d511 Mon Sep 17 00:00:00 2001 From: CJKmkp <2564608840@qq.com> Date: Sat, 28 Mar 2026 17:40:14 +0800 Subject: [PATCH] =?UTF-8?q?add:WinRT=E5=A2=A8=E8=BF=B9=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Ink Canvas/Helpers/InkRecognizeHelper.cs | 66 ++++- Ink Canvas/Helpers/InkShapeRecognition.cs | 83 +++++++ Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs | 234 ++++++++++++++++++ Ink Canvas/MainWindow.xaml | 13 + Ink Canvas/MainWindow.xaml.cs | 10 +- Ink Canvas/MainWindow_cs/MW_Settings.cs | 10 + Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 7 + .../MW_SimulatePressure&InkToShape.cs | 105 ++++---- Ink Canvas/Properties/Strings.enUS.xml | 5 + Ink Canvas/Properties/Strings.resx | 5 + Ink Canvas/Resources/Settings.cs | 6 + 11 files changed, 484 insertions(+), 60 deletions(-) create mode 100644 Ink Canvas/Helpers/InkShapeRecognition.cs create mode 100644 Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs diff --git a/Ink Canvas/Helpers/InkRecognizeHelper.cs b/Ink Canvas/Helpers/InkRecognizeHelper.cs index c98f267d..c9865604 100644 --- a/Ink Canvas/Helpers/InkRecognizeHelper.cs +++ b/Ink Canvas/Helpers/InkRecognizeHelper.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using System.Windows; using System.Windows.Ink; using System.Windows.Media; @@ -7,8 +7,8 @@ namespace Ink_Canvas.Helpers { public class InkRecognizeHelper { - //识别形状 - public static ShapeRecognizeResult RecognizeShape(StrokeCollection strokes) + /// IACore / IAWinFX 形状识别(典型用于 32 位进程)。 + public static ShapeRecognizeResult RecognizeShapeIACore(StrokeCollection strokes) { if (strokes == null || strokes.Count == 0) return default; @@ -52,6 +52,66 @@ namespace Ink_Canvas.Helpers return default; } + /// 兼容旧调用:等价于 + public static ShapeRecognizeResult RecognizeShape(StrokeCollection strokes) => + RecognizeShapeIACore(strokes); + + /// 按设置选择 WinRT 或 IACore,返回统一识别结果。 + public static InkShapeRecognitionResult RecognizeShapeUnified( + StrokeCollection strokes, + ShapeRecognitionEngineMode mode) + { + if (strokes == null || strokes.Count == 0) + return InkShapeRecognitionResult.Empty; + + if (ShapeRecognitionRouter.ResolveUseWinRt(mode)) + return WinRtInkShapeRecognizer.RecognizeShape(strokes); + + var legacy = RecognizeShapeIACore(strokes); + return FromIACoreOrEmpty(legacy); + } + + public static void WarmupShapeRecognition(ShapeRecognitionEngineMode mode) + { + try + { + if (ShapeRecognitionRouter.ResolveUseWinRt(mode)) + WinRtInkShapeRecognizer.Warmup(); + else + RecognizeShapeIACore(new StrokeCollection()); + } + catch + { + // 预热失败不影响启动 + } + } + + internal static InkShapeRecognitionResult FromIACoreOrEmpty(ShapeRecognizeResult legacy) + { + if (legacy?.InkDrawingNode == null) + return InkShapeRecognitionResult.Empty; + + var node = legacy.InkDrawingNode; + var shape = node.GetShape(); + var hot = ClonePointCollection(node.HotPoints); + return new InkShapeRecognitionResult( + node.GetShapeName(), + legacy.Centroid, + hot, + shape.Width, + shape.Height, + node.Strokes); + } + + private static PointCollection ClonePointCollection(PointCollection src) + { + var dst = new PointCollection(); + if (src == null) return dst; + foreach (System.Windows.Point p in src) + dst.Add(p); + return dst; + } + public static bool IsContainShapeType(string name) { if (name.Contains("Triangle") || name.Contains("Circle") || diff --git a/Ink Canvas/Helpers/InkShapeRecognition.cs b/Ink Canvas/Helpers/InkShapeRecognition.cs new file mode 100644 index 00000000..1e11c0dd --- /dev/null +++ b/Ink Canvas/Helpers/InkShapeRecognition.cs @@ -0,0 +1,83 @@ +using System; +using System.Windows; +using System.Windows.Ink; +using System.Windows.Media; +using OSVersionExtension; + +namespace Ink_Canvas.Helpers +{ + /// 墨迹形状识别后端:自动 / IACore / WinRT。 + public enum ShapeRecognitionEngineMode + { + Auto = 0, + IACore = 1, + WinRT = 2, + } + + public static class ShapeRecognitionRouter + { + public static bool ResolveUseWinRt(ShapeRecognitionEngineMode mode) + { + if (mode == ShapeRecognitionEngineMode.WinRT) return true; + if (mode == ShapeRecognitionEngineMode.IACore) return false; + return Environment.Is64BitProcess; + } + + public static bool ShouldRunShapeRecognition(bool inkToShapeEnabled, ShapeRecognitionEngineMode mode) + { + if (!inkToShapeEnabled) return false; + if (ResolveUseWinRt(mode)) + return OSVersion.GetOperatingSystem() >= OSVersionExtension.OperatingSystem.Windows10; + return !Environment.Is64BitProcess; + } + + public static ShapeRecognitionEngineMode FromSettingsInt(int value) + { + if (value == (int)ShapeRecognitionEngineMode.IACore) return ShapeRecognitionEngineMode.IACore; + if (value == (int)ShapeRecognitionEngineMode.WinRT) return ShapeRecognitionEngineMode.WinRT; + return ShapeRecognitionEngineMode.Auto; + } + } + + /// 与具体识别后端无关的形状识别结果,供统一纠正模块消费。 + public sealed class InkShapeRecognitionResult + { + public static readonly InkShapeRecognitionResult Empty = new InkShapeRecognitionResult(); + + private InkShapeRecognitionResult() + { + IsSuccess = false; + ShapeName = string.Empty; + Centroid = new Point(); + HotPoints = new PointCollection(); + StrokesToRemove = new StrokeCollection(); + } + + public InkShapeRecognitionResult( + string shapeName, + Point centroid, + PointCollection hotPoints, + double shapeWidth, + double shapeHeight, + StrokeCollection strokesToRemove) + { + ShapeName = shapeName ?? string.Empty; + Centroid = centroid; + HotPoints = hotPoints ?? new PointCollection(); + ShapeWidth = shapeWidth; + ShapeHeight = shapeHeight; + StrokesToRemove = strokesToRemove ?? new StrokeCollection(); + IsSuccess = StrokesToRemove.Count > 0 + && !string.IsNullOrEmpty(ShapeName) + && ShapeName != "Drawing"; + } + + public bool IsSuccess { get; } + public string ShapeName { get; } + public Point Centroid { get; set; } + public PointCollection HotPoints { get; } + public double ShapeWidth { get; } + public double ShapeHeight { get; } + public StrokeCollection StrokesToRemove { get; } + } +} diff --git a/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs b/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs new file mode 100644 index 00000000..2798088e --- /dev/null +++ b/Ink Canvas/Helpers/WinRtInkShapeRecognizer.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Threading.Tasks; +using System.Windows.Ink; +using System.Windows.Media; +using OSVersionExtension; +using WinRtInkAnalyzer = global::Windows.UI.Input.Inking.Analysis.InkAnalyzer; +using SysPoint = System.Windows.Point; + +namespace Ink_Canvas.Helpers +{ + /// 基于 Windows.UI.Input.Inking.Analysis 的形状识别(适用于 64 位进程等场景)。 + internal static class WinRtInkShapeRecognizer + { + public static bool IsApiAvailable => + OSVersion.GetOperatingSystem() >= OSVersionExtension.OperatingSystem.Windows10; + + public static void Warmup() + { + if (!IsApiAvailable) return; + try + { + RecognizeShape(new StrokeCollection()); + } + catch + { + // ignore + } + } + + public static InkShapeRecognitionResult RecognizeShape(StrokeCollection strokes) + { + if (!IsApiAvailable || strokes == null || strokes.Count == 0) + return InkShapeRecognitionResult.Empty; + + try + { + return RecognizeShapeAsync(strokes).GetAwaiter().GetResult(); + } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine(ex); + return InkShapeRecognitionResult.Empty; + } + } + + private static async Task RecognizeShapeAsync(StrokeCollection strokes) + { + var analyzer = new WinRtInkAnalyzer(); + foreach (Stroke s in strokes) + { + var inkStroke = CreateInkStrokeFromWpf(s); + if (inkStroke != null) + analyzer.AddDataForStroke(inkStroke); + } + + await analyzer.AnalyzeAsync().AsTask().ConfigureAwait(false); + + var drawing = FindPrimaryDrawing(analyzer); + if (drawing == null || + drawing.DrawingKind == global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Drawing) + return InkShapeRecognitionResult.Empty; + + var name = MapDrawingKindToShapeName(drawing.DrawingKind); + if (string.IsNullOrEmpty(name) || name == "Drawing") + return InkShapeRecognitionResult.Empty; + + var winPts = CopyWinRtPoints(drawing); + var hot = ToWpfPointCollection(winPts); + var c = drawing.Center; + var centroid = new SysPoint(c.X, c.Y); + BoundsFromPoints(winPts, out double w, out double h); + + var toRemove = new StrokeCollection(); + foreach (Stroke s in strokes) + toRemove.Add(s); + + return new InkShapeRecognitionResult(name, centroid, hot, w, h, toRemove); + } + + private static global::Windows.UI.Input.Inking.InkStroke CreateInkStrokeFromWpf(Stroke stroke) + { + if (stroke?.StylusPoints == null || stroke.StylusPoints.Count == 0) + return null; + + var da = stroke.DrawingAttributes; + var wda = new global::Windows.UI.Input.Inking.InkDrawingAttributes + { + PenTip = global::Windows.UI.Input.Inking.PenTipShape.Circle, + Color = global::Windows.UI.Color.FromArgb(da.Color.A, da.Color.R, da.Color.G, da.Color.B), + Size = new global::Windows.Foundation.Size((float)da.Width, (float)da.Height) + }; + + var builder = new global::Windows.UI.Input.Inking.InkStrokeBuilder(); + builder.SetDefaultDrawingAttributes(wda); + + var inkPoints = new List(stroke.StylusPoints.Count); + foreach (StylusPoint sp in stroke.StylusPoints) + { + var pi = sp.ToPoint(); + inkPoints.Add(new global::Windows.UI.Input.Inking.InkPoint( + new global::Windows.Foundation.Point((float)pi.X, (float)pi.Y), (float)sp.PressureFactor)); + } + + var transform = global::Windows.Foundation.Numerics.Matrix3x2.Identity; + return builder.CreateStrokeFromInkPoints(inkPoints, transform); + } + + private static global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing FindPrimaryDrawing( + WinRtInkAnalyzer analyzer) + { + global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing best = null; + double bestArea = -1; + Visit(analyzer.AnalysisRoot); + return best; + + void Visit(global::Windows.UI.Input.Inking.Analysis.IInkAnalysisNode node) + { + if (node == null) return; + + if (node is global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing d && + d.DrawingKind != global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Drawing) + { + double area = EstimateDrawingArea(d); + if (area > bestArea) + { + bestArea = area; + best = d; + } + } + + foreach (var child in node.Children) + Visit(child); + } + } + + private static double EstimateDrawingArea(global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing drawing) + { + var pts = CopyWinRtPoints(drawing); + BoundsFromPoints(pts, out double w, out double h); + return w * h; + } + + private static global::Windows.Foundation.Point[] CopyWinRtPoints( + global::Windows.UI.Input.Inking.Analysis.InkAnalysisInkDrawing drawing) + { + var src = drawing?.Points; + if (src == null) + return Array.Empty(); + + var n = src.Count; + if (n == 0) + return Array.Empty(); + + var arr = new global::Windows.Foundation.Point[n]; + for (var i = 0; i < n; i++) + arr[i] = src[i]; + return arr; + } + + private static void BoundsFromPoints( + System.Collections.Generic.IReadOnlyList points, + out double w, + out double h) + { + if (points == null || points.Count == 0) + { + w = h = 0; + return; + } + + double minX = double.MaxValue, maxX = double.MinValue, minY = double.MaxValue, maxY = double.MinValue; + for (int i = 0; i < points.Count; i++) + { + var pt = points[i]; + minX = Math.Min(minX, pt.X); + maxX = Math.Max(maxX, pt.X); + minY = Math.Min(minY, pt.Y); + maxY = Math.Max(maxY, pt.Y); + } + + w = Math.Max(0, maxX - minX); + h = Math.Max(0, maxY - minY); + } + + private static PointCollection ToWpfPointCollection( + System.Collections.Generic.IReadOnlyList points) + { + var hot = new PointCollection(); + if (points == null) return hot; + for (int i = 0; i < points.Count; i++) + { + var pt = points[i]; + hot.Add(new SysPoint(pt.X, pt.Y)); + } + + return hot; + } + + private static string MapDrawingKindToShapeName( + global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind kind) + { + switch (kind) + { + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Circle: + return "Circle"; + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Ellipse: + return "Ellipse"; + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Triangle: + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.IsoscelesTriangle: + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.EquilateralTriangle: + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.RightTriangle: + return "Triangle"; + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Rectangle: + return "Rectangle"; + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Square: + return "Square"; + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Diamond: + return "Diamond"; + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Trapezoid: + return "Trapezoid"; + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Parallelogram: + return "Parallelogram"; + case global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Quadrilateral: + return "Quadrilateral"; + default: + return kind == global::Windows.UI.Input.Inking.Analysis.InkAnalysisDrawingKind.Drawing + ? "Drawing" + : kind.ToString(); + } + } + } +} diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml index 221360e4..61fcd07d 100644 --- a/Ink Canvas/MainWindow.xaml +++ b/Ink Canvas/MainWindow.xaml @@ -1112,6 +1112,19 @@ IsOn="True" FontFamily="Microsoft YaHei UI" FontWeight="Bold" Toggled="ToggleSwitchEnableInkToShape_Toggled" /> + + + + + + + + + diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs index 98d4ab28..a2b56b2a 100644 --- a/Ink Canvas/MainWindow.xaml.cs +++ b/Ink Canvas/MainWindow.xaml.cs @@ -1322,11 +1322,13 @@ namespace Ink_Canvas BtnWhiteBoardSwitchPrevious.IsEnabled = CurrentWhiteboardIndex != 1; BorderInkReplayToolBox.Visibility = Visibility.Collapsed; - // 提前加载IA库,优化第一笔等待时间 - if (Settings.InkToShape.IsInkToShapeEnabled && !Environment.Is64BitProcess) + // 提前加载识别后端,优化第一笔等待时间 + if (ShapeRecognitionRouter.ShouldRunShapeRecognition( + Settings.InkToShape.IsInkToShapeEnabled, + ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine))) { - var strokeEmpty = new StrokeCollection(); - InkRecognizeHelper.RecognizeShape(strokeEmpty); + InkRecognizeHelper.WarmupShapeRecognition( + ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine)); } SystemEvents.DisplaySettingsChanged += SystemEventsOnDisplaySettingsChanged; diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs index 41415488..16d36e1c 100644 --- a/Ink Canvas/MainWindow_cs/MW_Settings.cs +++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs @@ -3952,6 +3952,16 @@ namespace Ink_Canvas SaveSettingsToFile(); } + private void ComboBoxShapeRecognitionEngine_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (!isLoaded || ComboBoxShapeRecognitionEngine == null) return; + int idx = ComboBoxShapeRecognitionEngine.SelectedIndex; + if (idx < 0) idx = 0; + if (idx > 2) idx = 2; + Settings.InkToShape.ShapeRecognitionEngine = idx; + SaveSettingsToFile(); + } + private void ToggleSwitchEnableInkToShapeNoFakePressureTriangle_Toggled(object sender, RoutedEventArgs e) { if (!isLoaded) return; diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs index 2a3e9247..e9b3a958 100644 --- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs +++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs @@ -1040,6 +1040,13 @@ namespace Ink_Canvas { ToggleSwitchEnableInkToShape.IsOn = Settings.InkToShape.IsInkToShapeEnabled; + if (ComboBoxShapeRecognitionEngine != null) + { + int eng = Settings.InkToShape.ShapeRecognitionEngine; + if (eng < 0 || eng > 2) eng = 0; + ComboBoxShapeRecognitionEngine.SelectedIndex = eng; + } + ToggleSwitchEnableInkToShapeNoFakePressureRectangle.IsOn = Settings.InkToShape.IsInkToShapeNoFakePressureRectangle; diff --git a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs index 243b64c9..6433a45a 100644 --- a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs +++ b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs @@ -139,7 +139,7 @@ namespace Ink_Canvas /// 5. 根据墨水风格设置模拟压感 /// 6. 应用高级贝塞尔曲线平滑(仅在未进行直线拉直时) /// - /// 注意:形状识别(圆形、椭圆、三角形、矩形等)仅在32位进程中可用。当 Environment.Is64BitProcess 为 true 时,形状识别功能会被禁用。 + /// 形状识别:IACore 典型用于 32 位进程;64 位可选用 WinRT(Windows 10+),详见设置「识别引擎」。 /// /// private void inkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e) @@ -384,7 +384,9 @@ namespace Ink_Canvas } } - if (Settings.InkToShape.IsInkToShapeEnabled && !Environment.Is64BitProcess) + if (ShapeRecognitionRouter.ShouldRunShapeRecognition( + Settings.InkToShape.IsInkToShapeEnabled, + ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine))) { void InkToShapeProcess() { @@ -403,32 +405,33 @@ namespace Ink_Canvas // 处理矩形参考线系统 ProcessRectangleGuideLines(e.Stroke); + var shapeMode = ShapeRecognitionRouter.FromSettingsInt(Settings.InkToShape.ShapeRecognitionEngine); var strokeReco = new StrokeCollection(); - var result = InkRecognizeHelper.RecognizeShape(newStrokes); + var result = InkRecognizeHelper.RecognizeShapeUnified(newStrokes, shapeMode); for (var i = newStrokes.Count - 1; i >= 0; i--) { strokeReco.Add(newStrokes[i]); - var newResult = InkRecognizeHelper.RecognizeShape(strokeReco); - if (newResult.InkDrawingNode.GetShapeName() == "Circle" || - newResult.InkDrawingNode.GetShapeName() == "Ellipse") + var newResult = InkRecognizeHelper.RecognizeShapeUnified(strokeReco, shapeMode); + if (newResult.IsSuccess && + (newResult.ShapeName == "Circle" || newResult.ShapeName == "Ellipse")) { result = newResult; break; } - //Label.Visibility = Visibility.Visible; - //Label.Content = circles.Count.ToString() + "\n" + newResult.InkDrawingNode.GetShapeName(); } - if (result.InkDrawingNode.GetShapeName() == "Circle" && + if (!result.IsSuccess) + return; + + if (result.ShapeName == "Circle" && Settings.InkToShape.IsInkToShapeRounded) { - var shape = result.InkDrawingNode.GetShape(); - if (shape.Width > 75) + if (result.ShapeWidth > 75) { foreach (var circle in circles) //判断是否画同心圆 - if (Math.Abs(result.Centroid.X - circle.Centroid.X) / shape.Width < 0.12 && - Math.Abs(result.Centroid.Y - circle.Centroid.Y) / shape.Width < 0.12) + 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; @@ -441,8 +444,8 @@ namespace Ink_Canvas (result.Centroid.Y - circle.Centroid.Y); d = Math.Sqrt(d); //判断是否画外切圆 - var x = shape.Width / 2.0 + circle.R - d; - if (Math.Abs(x) / shape.Width < 0.1) + 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; @@ -452,8 +455,8 @@ namespace Ink_Canvas } //判断是否画外切圆 - x = Math.Abs(circle.R - shape.Width / 2.0) - d; - if (Math.Abs(x) / shape.Width < 0.1) + 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; @@ -463,33 +466,29 @@ namespace Ink_Canvas } } - var iniP = new Point(result.Centroid.X - shape.Width / 2, - result.Centroid.Y - shape.Height / 2); - var endP = new Point(result.Centroid.X + shape.Width / 2, - result.Centroid.Y + shape.Height / 2); + 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() }; - circles.Add(new Circle(result.Centroid, shape.Width / 2.0, stroke)); + circles.Add(new Circle(result.Centroid, result.ShapeWidth / 2.0, stroke)); SetNewBackupOfStroke(); _currentCommitType = CommitReason.ShapeRecognition; - inkCanvas.Strokes.Remove(result.InkDrawingNode.Strokes); + inkCanvas.Strokes.Remove(result.StrokesToRemove); inkCanvas.Strokes.Add(stroke); _currentCommitType = CommitReason.UserInput; newStrokes = new StrokeCollection(); } } - else if (result.InkDrawingNode.GetShapeName().Contains("Ellipse") && + else if (result.ShapeName.Contains("Ellipse") && Settings.InkToShape.IsInkToShapeRounded) { - var shape = result.InkDrawingNode.GetShape(); - //var shape1 = result.InkDrawingNode.GetShape(); - //shape1.Fill = Brushes.Gray; - //Canvas.Children.Add(shape1); - var p = result.InkDrawingNode.HotPoints; + var p = result.HotPoints; var a = GetDistance(p[0], p[2]) / 2; //长半轴 var b = GetDistance(p[1], p[3]) / 2; //短半轴 if (a < b) @@ -502,12 +501,12 @@ namespace Ink_Canvas result.Centroid = new Point((p[0].X + p[2].X) / 2, (p[0].Y + p[2].Y) / 2); var needRotation = true; - if (shape.Width > 75 || (shape.Height > 75 && p.Count == 4)) + if (result.ShapeWidth > 75 || (result.ShapeHeight > 75 && p.Count == 4)) { - var iniP = new Point(result.Centroid.X - shape.Width / 2, - result.Centroid.Y - shape.Height / 2); - var endP = new Point(result.Centroid.X + shape.Width / 2, - result.Centroid.Y + shape.Height / 2); + 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); foreach (var circle in circles) //判断是否画同心椭圆 @@ -515,15 +514,15 @@ namespace Ink_Canvas Math.Abs(result.Centroid.Y - circle.Centroid.Y) / a < 0.2) { result.Centroid = circle.Centroid; - iniP = new Point(result.Centroid.X - shape.Width / 2, - result.Centroid.Y - shape.Height / 2); - endP = new Point(result.Centroid.X + shape.Width / 2, - result.Centroid.Y + shape.Height / 2); + 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 (shape.Width >= shape.Height) + if (result.ShapeWidth >= result.ShapeHeight) { iniP.X = result.Centroid.X - circle.R; endP.X = result.Centroid.X + circle.R; @@ -559,7 +558,7 @@ namespace Ink_Canvas SetNewBackupOfStroke(); _currentCommitType = CommitReason.ShapeRecognition; - inkCanvas.Strokes.Remove(result.InkDrawingNode.Strokes); + inkCanvas.Strokes.Remove(result.StrokesToRemove); newStrokes = new StrokeCollection(); var _pointList = GenerateEllipseGeometry(iniP, endP, false); @@ -623,23 +622,23 @@ namespace Ink_Canvas SetNewBackupOfStroke(); _currentCommitType = CommitReason.ShapeRecognition; - inkCanvas.Strokes.Remove(result.InkDrawingNode.Strokes); + inkCanvas.Strokes.Remove(result.StrokesToRemove); inkCanvas.Strokes.Add(stroke); _currentCommitType = CommitReason.UserInput; GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed; newStrokes = new StrokeCollection(); } } - else if (result.InkDrawingNode.GetShapeName().Contains("Triangle") && + else if (result.ShapeName.Contains("Triangle") && Settings.InkToShape.IsInkToShapeTriangle) { var shape = result.InkDrawingNode.GetShape(); - var p = result.InkDrawingNode.HotPoints; + 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 || 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) && - result.InkDrawingNode.HotPoints.Count == 3) + result.HotPoints.Count == 3) { //纠正垂直与水平关系 var newPoints = FixPointsDirection(p[0], p[1]); @@ -661,27 +660,27 @@ namespace Ink_Canvas }; SetNewBackupOfStroke(); _currentCommitType = CommitReason.ShapeRecognition; - inkCanvas.Strokes.Remove(result.InkDrawingNode.Strokes); + inkCanvas.Strokes.Remove(result.StrokesToRemove); inkCanvas.Strokes.Add(stroke); _currentCommitType = CommitReason.UserInput; GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed; newStrokes = new StrokeCollection(); } } - else if ((result.InkDrawingNode.GetShapeName().Contains("Rectangle") || - result.InkDrawingNode.GetShapeName().Contains("Diamond") || - result.InkDrawingNode.GetShapeName().Contains("Parallelogram") || - result.InkDrawingNode.GetShapeName().Contains("Square") || - result.InkDrawingNode.GetShapeName().Contains("Trapezoid")) && + else if ((result.ShapeName.Contains("Rectangle") || + result.ShapeName.Contains("Diamond") || + result.ShapeName.Contains("Parallelogram") || + result.ShapeName.Contains("Square") || + result.ShapeName.Contains("Trapezoid")) && Settings.InkToShape.IsInkToShapeRectangle) { var shape = result.InkDrawingNode.GetShape(); - var p = result.InkDrawingNode.HotPoints; + 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 || 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) && - result.InkDrawingNode.HotPoints.Count == 4) + result.HotPoints.Count == 4) { //纠正垂直与水平关系 var newPoints = FixPointsDirection(p[0], p[1]); @@ -706,7 +705,7 @@ namespace Ink_Canvas }; SetNewBackupOfStroke(); _currentCommitType = CommitReason.ShapeRecognition; - inkCanvas.Strokes.Remove(result.InkDrawingNode.Strokes); + inkCanvas.Strokes.Remove(result.StrokesToRemove); inkCanvas.Strokes.Add(stroke); _currentCommitType = CommitReason.UserInput; GridInkCanvasSelectionCover.Visibility = Visibility.Collapsed; diff --git a/Ink Canvas/Properties/Strings.enUS.xml b/Ink Canvas/Properties/Strings.enUS.xml index 0b54838b..82bf3d3f 100644 --- a/Ink Canvas/Properties/Strings.enUS.xml +++ b/Ink Canvas/Properties/Strings.enUS.xml @@ -224,6 +224,11 @@ # Low: larger area/more touches required (less false positive); High: easier to trigger but may mis-detect fingers. Ink correction Enable ink recognition + Recognition engine + # Auto: WinRT on 64-bit (Windows 10+), IACore on 32-bit. You can force IACore or WinRT. + Auto + IACore + WinRT Ink stroke prediction # When on, shows a short ahead-of-stroke hint while inking. Choose Auto (speed-based), 25 ms, or 50 ms lead. Block fake pressure on corrected rectangles diff --git a/Ink Canvas/Properties/Strings.resx b/Ink Canvas/Properties/Strings.resx index 0db71c7a..f61e5937 100644 --- a/Ink Canvas/Properties/Strings.resx +++ b/Ink Canvas/Properties/Strings.resx @@ -239,6 +239,11 @@ # 低敏感度:需要更大的触摸面积和更多触摸点,减少误判;高敏感度:更容易触发手掌擦,但可能误判手指。 墨迹纠正 启用墨迹识别 + 识别引擎 + # 自动:64 位进程使用 WinRT(Windows 10+),32 位使用 IACore。可强制指定 IACore 或 WinRT。 + 自动 + IACore + WinRT 墨迹预测 # 开启后可在书写时显示短暂外推预览线。打开开关后可选择:自动(随速度调整)、固定 25ms 或 50ms 提前量。 阻止矫正后的矩形带有模拟压感值 diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs index f6fe9229..678e9208 100644 --- a/Ink Canvas/Resources/Settings.cs +++ b/Ink Canvas/Resources/Settings.cs @@ -762,6 +762,12 @@ namespace Ink_Canvas public double LineStraightenSensitivity { get; set; } = 0.20; [JsonProperty("lineNormalizationThreshold")] public double LineNormalizationThreshold { get; set; } = 0.5; + + /// + /// 形状识别后端:0=自动(x64 用 WinRT,x86 用 IACore),1=IACore,2=WinRT。 + /// + [JsonProperty("shapeRecognitionEngine")] + public int ShapeRecognitionEngine { get; set; } } public class RandSettings