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