Merge branch 'net6' of https://github.com/InkCanvasForClass/community into net6
This commit is contained in:
@@ -44,5 +44,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.7.18.9")]
|
||||
[assembly: AssemblyFileVersion("1.7.18.9")]
|
||||
[assembly: AssemblyVersion("1.7.18.10")]
|
||||
[assembly: AssemblyFileVersion("1.7.18.10")]
|
||||
|
||||
@@ -152,6 +152,9 @@ namespace Ink_Canvas.Helpers
|
||||
|
||||
var node = legacy.InkDrawingNode;
|
||||
var shape = node.GetShape();
|
||||
if (shape == null)
|
||||
return InkShapeRecognitionResult.Empty;
|
||||
|
||||
var hot = ClonePointCollection(node.HotPoints);
|
||||
return new InkShapeRecognitionResult(
|
||||
node.GetShapeName(),
|
||||
@@ -173,6 +176,9 @@ namespace Ink_Canvas.Helpers
|
||||
|
||||
public static bool IsContainShapeType(string name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return false;
|
||||
|
||||
if (name.Contains("Triangle") || name.Contains("Circle") ||
|
||||
name.Contains("Rectangle") || name.Contains("Diamond") ||
|
||||
name.Contains("Parallelogram") || name.Contains("Square")
|
||||
|
||||
@@ -29,31 +29,13 @@ namespace Ink_Canvas.Helpers
|
||||
public static bool IsApiAvailable =>
|
||||
OSVersion.GetOperatingSystem() >= OSVersionExtension.OperatingSystem.Windows10;
|
||||
|
||||
/// <summary>
|
||||
/// 启动阶段不再预热线程内 WinRT 手写管线。历史上曾用 <see cref="WinRtInkShapeRecognizer.CreateMinimalWarmupStrokeCollection"/> 跑全链路,
|
||||
/// 会显著拖慢启动;与更早的「空 <see cref="StrokeCollection"/>」一样,此处不再在 Idle 上做任何工作。
|
||||
/// 首次真正需要手写识别时由 <see cref="RecognizeHandwritingAsync"/> 承担冷启动成本。
|
||||
/// </summary>
|
||||
public static void Warmup()
|
||||
{
|
||||
if (!IsApiAvailable || !Environment.Is64BitProcess) return;
|
||||
try
|
||||
{
|
||||
var d = Application.Current?.Dispatcher;
|
||||
if (d == null) return;
|
||||
d.BeginInvoke(new Action(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await RecognizeHandwritingAsync(
|
||||
WinRtInkShapeRecognizer.CreateMinimalWarmupStrokeCollection(),
|
||||
verboseTrace: false).ConfigureAwait(true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -124,6 +124,9 @@ namespace Ink_Canvas.Helpers
|
||||
return null;
|
||||
|
||||
var da = stroke.DrawingAttributes;
|
||||
if (da == null)
|
||||
return null;
|
||||
|
||||
var wda = new global::Windows.UI.Input.Inking.InkDrawingAttributes
|
||||
{
|
||||
PenTip = global::Windows.UI.Input.Inking.PenTipShape.Circle,
|
||||
|
||||
@@ -1487,6 +1487,7 @@
|
||||
<TextBlock Text="{i18n:I18n Key=Advanced_TouchMultiplierHint}"
|
||||
Foreground="#a1a1aa" TextWrapping="Wrap" Width="460" />
|
||||
<controls:LabeledToggleSwitch x:Name="ToggleSwitchIsSpecialScreen" Label="{i18n:I18n Key=Advanced_SpecialScreenMode}" IsOn="True" Toggled="ToggleSwitchIsSpecialScreen_OnToggled"/>
|
||||
<controls:LabeledToggleSwitch x:Name="ToggleSwitchDisableHardwareAcceleration" Label="{i18n:I18n Key=Canvas_DisableHardwareAcceleration}" Hint="{i18n:I18n Key=Canvas_DisableHardwareAccelerationHint}" IsOn="False" Toggled="ToggleSwitchDisableHardwareAcceleration_Toggled"/>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Foreground="#fafafa" Text="{i18n:I18n Key=Advanced_TouchMultiplier}" VerticalAlignment="Center"
|
||||
FontSize="14" Margin="0,0,16,0" />
|
||||
|
||||
@@ -3733,6 +3733,9 @@ namespace Ink_Canvas
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
if (Settings.ModeSettings.IsPPTOnlyMode)
|
||||
{
|
||||
if (TrayTemporaryShowUntilUtc.HasValue && DateTime.UtcNow < TrayTemporaryShowUntilUtc.Value)
|
||||
|
||||
@@ -1887,54 +1887,22 @@ namespace Ink_Canvas
|
||||
|
||||
|
||||
|
||||
// 注释掉这些方法,因为对应的UI控件还没有在XAML中定义
|
||||
/*
|
||||
private void ToggleSwitchAsyncInkSmoothing_Toggled(object sender, RoutedEventArgs e) {
|
||||
private void ToggleSwitchDisableHardwareAcceleration_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.UseAsyncInkSmoothing = ToggleSwitchAsyncInkSmoothing.IsOn;
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ToggleSwitchHardwareAcceleration_Toggled(object sender, RoutedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.UseHardwareAcceleration = ToggleSwitchHardwareAcceleration.IsOn;
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ComboBoxInkSmoothingQuality_SelectionChanged(object sender, SelectionChangedEventArgs e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.InkSmoothingQuality = ComboBoxInkSmoothingQuality.SelectedIndex;
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void SliderMaxConcurrentTasks_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) {
|
||||
if (!isLoaded) return;
|
||||
Settings.Canvas.MaxConcurrentSmoothingTasks = (int)SliderMaxConcurrentTasks.Value;
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
|
||||
private void ButtonApplyRecommendedSettings_Click(object sender, RoutedEventArgs e) {
|
||||
// 应用推荐的性能设置
|
||||
Helpers.InkSmoothingManager.ApplyRecommendedSettings();
|
||||
LoadSettings(false);
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
|
||||
ShowNotification("已应用推荐的性能设置");
|
||||
}
|
||||
|
||||
private void ButtonShowPerformanceStats_Click(object sender, RoutedEventArgs e) {
|
||||
if (_inkSmoothingManager != null)
|
||||
var isOnObj = sender?.GetType().GetProperty("IsOn")?.GetValue(sender);
|
||||
if (isOnObj is bool isOn)
|
||||
{
|
||||
var stats = _inkSmoothingManager.GetPerformanceStats();
|
||||
ShowNotification($"性能统计: {stats}");
|
||||
Settings.Canvas.UseHardwareAcceleration = !isOn;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_inkSmoothingManager?.UpdateConfig();
|
||||
SaveSettingsToFile();
|
||||
}
|
||||
*/
|
||||
|
||||
private void ToggleSwitchAutoSaveStrokesInPowerPoint_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
@@ -699,22 +699,7 @@ namespace Ink_Canvas
|
||||
drawingAttributes.FitToCurve = false;
|
||||
}
|
||||
|
||||
// 注释掉新的墨迹平滑性能设置,因为UI控件还没有定义
|
||||
/*
|
||||
// 初始化新的墨迹平滑性能设置
|
||||
ToggleSwitchAsyncInkSmoothing.IsOn = Settings.Canvas.UseAsyncInkSmoothing;
|
||||
ToggleSwitchHardwareAcceleration.IsOn = Settings.Canvas.UseHardwareAcceleration;
|
||||
ComboBoxInkSmoothingQuality.SelectedIndex = Settings.Canvas.InkSmoothingQuality;
|
||||
SliderMaxConcurrentTasks.Value = Settings.Canvas.MaxConcurrentSmoothingTasks > 0 ?
|
||||
Settings.Canvas.MaxConcurrentSmoothingTasks : Environment.ProcessorCount;
|
||||
|
||||
// 检查硬件加速支持
|
||||
if (!Helpers.InkSmoothingManager.IsHardwareAccelerationSupported())
|
||||
{
|
||||
ToggleSwitchHardwareAcceleration.IsEnabled = false;
|
||||
// 可以添加提示文本说明硬件加速不可用
|
||||
}
|
||||
*/
|
||||
ToggleSwitchDisableHardwareAcceleration.IsOn = !Settings.Canvas.UseHardwareAcceleration;
|
||||
|
||||
// 初始化直线自动拉直相关设置
|
||||
// 直线拉直灵敏度也在这里初始化,即使它存储在InkToShape中
|
||||
|
||||
@@ -718,6 +718,18 @@ namespace Ink_Canvas
|
||||
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 &&
|
||||
@@ -805,13 +817,17 @@ namespace Ink_Canvas
|
||||
}
|
||||
}
|
||||
|
||||
//纠正垂直与水平关系
|
||||
var newPoints = FixPointsDirection(p[0], p[2]);
|
||||
p[0] = newPoints[0];
|
||||
p[2] = newPoints[1];
|
||||
newPoints = FixPointsDirection(p[1], p[3]);
|
||||
p[1] = newPoints[0];
|
||||
p[3] = newPoints[1];
|
||||
// 用反推参数替换中心与长短轴(比 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);
|
||||
@@ -823,9 +839,8 @@ namespace Ink_Canvas
|
||||
if (needRotation)
|
||||
{
|
||||
var m = new Matrix();
|
||||
var fe = e.Source as FrameworkElement;
|
||||
var tanTheta = (p[2].Y - p[0].Y) / (p[2].X - p[0].X);
|
||||
var theta = Math.Atan(tanTheta);
|
||||
// 优先使用反推参数角度;否则用端点向量角度(使用 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);
|
||||
}
|
||||
@@ -2282,6 +2297,124 @@ namespace Ink_Canvas
|
||||
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;
|
||||
}
|
||||
|
||||
public StylusPointCollection GenerateFakePressureTriangle(StylusPointCollection points)
|
||||
{
|
||||
var newPoint = new StylusPointCollection();
|
||||
|
||||
@@ -43,5 +43,5 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.7.18.9")]
|
||||
[assembly: AssemblyFileVersion("1.7.18.9")]
|
||||
[assembly: AssemblyVersion("1.7.18.10")]
|
||||
[assembly: AssemblyFileVersion("1.7.18.10")]
|
||||
|
||||
@@ -996,6 +996,12 @@
|
||||
<data name="Canvas_GroupTitle" xml:space="preserve">
|
||||
<value>Canvas</value>
|
||||
</data>
|
||||
<data name="Canvas_DisableHardwareAcceleration" xml:space="preserve">
|
||||
<value>Disable hardware acceleration</value>
|
||||
</data>
|
||||
<data name="Canvas_DisableHardwareAccelerationHint" xml:space="preserve">
|
||||
<value># Improves compatibility but may reduce performance; some effects may require an app restart to fully apply.</value>
|
||||
</data>
|
||||
<data name="Canvas_ShowCursor" xml:space="preserve">
|
||||
<value>Show pen cursor</value>
|
||||
</data>
|
||||
|
||||
@@ -576,6 +576,12 @@
|
||||
<data name="InkRecog_ShapeEngineHint" xml:space="preserve">
|
||||
<value> 自动:64 位进程使用 WinRT(Windows 10+),32 位使用 IACore。可强制指定 IACore 或 WinRT。</value>
|
||||
</data>
|
||||
<data name="InkRecog_HandwritingBeautify" xml:space="preserve">
|
||||
<value>手写体纠正</value>
|
||||
</data>
|
||||
<data name="InkRecog_HandwritingBeautifyHint" xml:space="preserve">
|
||||
<value># 开启后,将对手写笔画进行更平滑、更工整的纠正处理(基于 WinRT)。</value>
|
||||
</data>
|
||||
<data name="InkRecog_ShapeEngineAuto" xml:space="preserve">
|
||||
<value>自动</value>
|
||||
</data>
|
||||
@@ -1038,6 +1044,12 @@
|
||||
<data name="Canvas_GroupTitle" xml:space="preserve">
|
||||
<value>画板</value>
|
||||
</data>
|
||||
<data name="Canvas_DisableHardwareAcceleration" xml:space="preserve">
|
||||
<value>关闭硬件加速</value>
|
||||
</data>
|
||||
<data name="Canvas_DisableHardwareAccelerationHint" xml:space="preserve">
|
||||
<value># 关闭后可提升兼容性,但可能降低性能;部分效果可能需要重启程序后完全生效。</value>
|
||||
</data>
|
||||
<data name="Canvas_ShowCursor" xml:space="preserve">
|
||||
<value>显示画笔光标</value>
|
||||
</data>
|
||||
|
||||
Reference in New Issue
Block a user