From d5142ad82c8202f82884bb6d647605496fab55b3 Mon Sep 17 00:00:00 2001
From: unknown <2564608840@qq.com>
Date: Sun, 20 Jul 2025 15:21:59 +0800
Subject: [PATCH] =?UTF-8?q?improve:=E5=A2=A8=E8=BF=B9=E5=B9=B3=E6=BB=91?=
=?UTF-8?q?=E6=96=B9=E6=A1=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Ink Canvas/Helpers/AdvancedBezierSmoothing.cs | 291 ++++++++++++++++++
Ink Canvas/MainWindow.xaml | 42 ++-
Ink Canvas/MainWindow.xaml.cs | 10 +-
Ink Canvas/MainWindow_cs/MW_Settings.cs | 47 ++-
Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs | 26 +-
Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs | 32 +-
.../MW_SimulatePressure&InkToShape.cs | 33 +-
Ink Canvas/Resources/Settings.cs | 10 +-
8 files changed, 479 insertions(+), 12 deletions(-)
create mode 100644 Ink Canvas/Helpers/AdvancedBezierSmoothing.cs
diff --git a/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs b/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs
new file mode 100644
index 00000000..c495af4d
--- /dev/null
+++ b/Ink Canvas/Helpers/AdvancedBezierSmoothing.cs
@@ -0,0 +1,291 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Windows;
+using System.Windows.Ink;
+using System.Windows.Input;
+using System.Windows.Media;
+using Point = System.Windows.Point;
+
+namespace Ink_Canvas.Helpers
+{
+ ///
+ /// 高级贝塞尔曲线平滑算法
+ /// 用于解决墨迹闪烁问题,提供更平滑的笔迹效果
+ ///
+ public class AdvancedBezierSmoothing
+ {
+ ///
+ /// 平滑强度 (0.0 - 1.0)
+ ///
+ public double SmoothingStrength { get; set; } = 0.6;
+
+ ///
+ /// 张力参数 (0.0 - 1.0)
+ ///
+ public double Tension { get; set; } = 0.5;
+
+ ///
+ /// 是否启用自适应平滑
+ ///
+ public bool EnableAdaptiveSmoothing { get; set; } = true;
+
+ ///
+ /// 最小点间距阈值
+ ///
+ public double MinPointDistance { get; set; } = 2.0;
+
+ ///
+ /// 最大点间距阈值
+ ///
+ public double MaxPointDistance { get; set; } = 50.0;
+
+ ///
+ /// 对笔画进行高级贝塞尔曲线平滑处理
+ ///
+ /// 原始笔画
+ /// 平滑后的笔画
+ public Stroke SmoothStroke(Stroke stroke)
+ {
+ if (stroke == null || stroke.StylusPoints.Count < 3)
+ return stroke;
+
+ var originalPoints = stroke.StylusPoints.ToList();
+ var smoothedPoints = new List();
+
+ // 第一步:点过滤和重采样
+ var filteredPoints = FilterAndResamplePoints(originalPoints);
+
+ // 第二步:计算控制点
+ var controlPoints = CalculateControlPoints(filteredPoints);
+
+ // 第三步:生成平滑曲线点
+ var curvePoints = GenerateCurvePoints(filteredPoints, controlPoints);
+
+ // 第四步:创建新的笔画
+ var newStylusPoints = new StylusPointCollection(curvePoints);
+ var smoothedStroke = new Stroke(newStylusPoints)
+ {
+ DrawingAttributes = stroke.DrawingAttributes.Clone()
+ };
+
+ return smoothedStroke;
+ }
+
+ ///
+ /// 过滤和重采样点
+ ///
+ private List FilterAndResamplePoints(List points)
+ {
+ var filteredPoints = new List();
+
+ if (points.Count == 0) return filteredPoints;
+
+ // 添加第一个点
+ filteredPoints.Add(points[0]);
+
+ for (int i = 1; i < points.Count; i++)
+ {
+ var currentPoint = points[i];
+ var lastPoint = filteredPoints[filteredPoints.Count - 1];
+
+ double distance = GetDistance(lastPoint.ToPoint(), currentPoint.ToPoint());
+
+ // 如果距离太近,跳过
+ if (distance < MinPointDistance)
+ continue;
+
+ // 如果距离太远,插入中间点
+ if (distance > MaxPointDistance)
+ {
+ int segments = (int)(distance / MaxPointDistance) + 1;
+ for (int j = 1; j < segments; j++)
+ {
+ double ratio = (double)j / segments;
+ var interpolatedPoint = InterpolatePoint(lastPoint, currentPoint, ratio);
+ filteredPoints.Add(interpolatedPoint);
+ }
+ }
+
+ filteredPoints.Add(currentPoint);
+ }
+
+ return filteredPoints;
+ }
+
+ ///
+ /// 计算贝塞尔曲线的控制点
+ ///
+ private List CalculateControlPoints(List points)
+ {
+ var controlPoints = new List();
+
+ if (points.Count < 2) return controlPoints;
+
+ for (int i = 0; i < points.Count; i++)
+ {
+ Point currentPoint = points[i].ToPoint();
+ Point controlPoint;
+
+ if (i == 0)
+ {
+ // 第一个点的控制点
+ Point nextPoint = points[i + 1].ToPoint();
+ controlPoint = new Point(
+ currentPoint.X + (nextPoint.X - currentPoint.X) * Tension * 0.5,
+ currentPoint.Y + (nextPoint.Y - currentPoint.Y) * Tension * 0.5
+ );
+ }
+ else if (i == points.Count - 1)
+ {
+ // 最后一个点的控制点
+ Point prevPoint = points[i - 1].ToPoint();
+ controlPoint = new Point(
+ currentPoint.X + (currentPoint.X - prevPoint.X) * Tension * 0.5,
+ currentPoint.Y + (currentPoint.Y - prevPoint.Y) * Tension * 0.5
+ );
+ }
+ else
+ {
+ // 中间点的控制点
+ Point prevPoint = points[i - 1].ToPoint();
+ Point nextPoint = points[i + 1].ToPoint();
+
+ // 计算切线方向
+ double tangentX = (nextPoint.X - prevPoint.X) * 0.5;
+ double tangentY = (nextPoint.Y - prevPoint.Y) * 0.5;
+
+ // 应用张力参数
+ controlPoint = new Point(
+ currentPoint.X + tangentX * Tension,
+ currentPoint.Y + tangentY * Tension
+ );
+ }
+
+ controlPoints.Add(controlPoint);
+ }
+
+ return controlPoints;
+ }
+
+ ///
+ /// 生成曲线点
+ ///
+ private List GenerateCurvePoints(List points, List controlPoints)
+ {
+ var curvePoints = new List();
+
+ if (points.Count < 2) return curvePoints;
+
+ // 为每个线段生成贝塞尔曲线点
+ for (int i = 0; i < points.Count - 1; i++)
+ {
+ var startPoint = points[i];
+ var endPoint = points[i + 1];
+ var startControl = controlPoints[i];
+ var endControl = controlPoints[i + 1];
+
+ // 计算自适应步长
+ double distance = GetDistance(startPoint.ToPoint(), endPoint.ToPoint());
+ int steps = Math.Max(3, Math.Min(20, (int)(distance / 5.0)));
+
+ // 生成贝塞尔曲线点
+ for (int j = 0; j <= steps; j++)
+ {
+ double t = (double)j / steps;
+ var curvePoint = CalculateBezierPoint(startPoint.ToPoint(), startControl, endControl, endPoint.ToPoint(), t);
+
+ // 插值压感值
+ float pressure = InterpolatePressure(startPoint.PressureFactor, endPoint.PressureFactor, t);
+
+ var stylusPoint = new StylusPoint(curvePoint.X, curvePoint.Y, pressure);
+ curvePoints.Add(stylusPoint);
+ }
+ }
+
+ return curvePoints;
+ }
+
+ ///
+ /// 计算贝塞尔曲线上的点
+ ///
+ private Point CalculateBezierPoint(Point p0, Point p1, Point p2, Point p3, double t)
+ {
+ double u = 1 - t;
+ double tt = t * t;
+ double uu = u * u;
+ double uuu = uu * u;
+ double ttt = tt * t;
+
+ Point point = new Point();
+ point.X = uuu * p0.X + 3 * uu * t * p1.X + 3 * u * tt * p2.X + ttt * p3.X;
+ point.Y = uuu * p0.Y + 3 * uu * t * p1.Y + 3 * u * tt * p2.Y + ttt * p3.Y;
+
+ return point;
+ }
+
+ ///
+ /// 计算两点间距离
+ ///
+ private double GetDistance(Point p1, Point p2)
+ {
+ return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
+ }
+
+ ///
+ /// 插值两点间的点
+ ///
+ private StylusPoint InterpolatePoint(StylusPoint p1, StylusPoint p2, double ratio)
+ {
+ return new StylusPoint(
+ p1.X + (p2.X - p1.X) * ratio,
+ p1.Y + (p2.Y - p1.Y) * ratio,
+ InterpolatePressure(p1.PressureFactor, p2.PressureFactor, ratio)
+ );
+ }
+
+ ///
+ /// 插值压感值
+ ///
+ private float InterpolatePressure(float p1, float p2, double ratio)
+ {
+ return (float)(p1 + (p2 - p1) * ratio);
+ }
+
+ ///
+ /// 应用自适应平滑
+ ///
+ private void ApplyAdaptiveSmoothing(List points)
+ {
+ if (!EnableAdaptiveSmoothing || points.Count < 3)
+ return;
+
+ // 计算笔迹的速度变化
+ var speeds = new List();
+ for (int i = 1; i < points.Count - 1; i++)
+ {
+ var prev = points[i - 1].ToPoint();
+ var curr = points[i].ToPoint();
+ var next = points[i + 1].ToPoint();
+
+ double speed1 = GetDistance(prev, curr);
+ double speed2 = GetDistance(curr, next);
+ double avgSpeed = (speed1 + speed2) / 2.0;
+
+ speeds.Add(avgSpeed);
+ }
+
+ // 根据速度调整平滑强度
+ if (speeds.Count > 0)
+ {
+ double avgSpeed = speeds.Average();
+ double maxSpeed = speeds.Max();
+ double minSpeed = speeds.Min();
+
+ // 速度变化越大,平滑强度越小
+ double speedVariation = (maxSpeed - minSpeed) / avgSpeed;
+ SmoothingStrength = Math.Max(0.1, Math.Min(0.9, SmoothingStrength * (1.0 - speedVariation * 0.3)));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml
index f663fae4..6a165a37 100644
--- a/Ink Canvas/MainWindow.xaml
+++ b/Ink Canvas/MainWindow.xaml
@@ -781,12 +781,48 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Ink Canvas/MainWindow.xaml.cs b/Ink Canvas/MainWindow.xaml.cs
index 29ada085..16e045e6 100644
--- a/Ink Canvas/MainWindow.xaml.cs
+++ b/Ink Canvas/MainWindow.xaml.cs
@@ -143,7 +143,15 @@ namespace Ink_Canvas {
drawingAttributes.Height = 2.5;
drawingAttributes.Width = 2.5;
drawingAttributes.IsHighlighter = false;
- drawingAttributes.FitToCurve = Settings.Canvas.FitToCurve;
+ // 默认使用高级贝塞尔曲线平滑,如果未启用则使用原来的FitToCurve
+ if (Settings.Canvas.UseAdvancedBezierSmoothing)
+ {
+ drawingAttributes.FitToCurve = false;
+ }
+ else
+ {
+ drawingAttributes.FitToCurve = Settings.Canvas.FitToCurve;
+ }
inkCanvas.EditingMode = InkCanvasEditingMode.Ink;
inkCanvas.Gesture += InkCanvas_Gesture;
diff --git a/Ink Canvas/MainWindow_cs/MW_Settings.cs b/Ink Canvas/MainWindow_cs/MW_Settings.cs
index 81084903..c330aff4 100644
--- a/Ink Canvas/MainWindow_cs/MW_Settings.cs
+++ b/Ink Canvas/MainWindow_cs/MW_Settings.cs
@@ -1285,6 +1285,47 @@ namespace Ink_Canvas {
if (!isLoaded) return;
drawingAttributes.FitToCurve = ToggleSwitchFitToCurve.IsOn;
Settings.Canvas.FitToCurve = ToggleSwitchFitToCurve.IsOn;
+
+ // 启用原来的FitToCurve时自动禁用高级贝塞尔平滑
+ if (ToggleSwitchFitToCurve.IsOn)
+ {
+ ToggleSwitchAdvancedBezierSmoothing.IsOn = false;
+ Settings.Canvas.UseAdvancedBezierSmoothing = false;
+ }
+
+ SaveSettingsToFile();
+ }
+
+ private void ToggleSwitchAdvancedBezierSmoothing_Toggled(object sender, RoutedEventArgs e) {
+ if (!isLoaded) return;
+ Settings.Canvas.UseAdvancedBezierSmoothing = ToggleSwitchAdvancedBezierSmoothing.IsOn;
+
+ // 启用高级贝塞尔平滑时自动禁用原来的FitToCurve
+ if (ToggleSwitchAdvancedBezierSmoothing.IsOn)
+ {
+ ToggleSwitchFitToCurve.IsOn = false;
+ Settings.Canvas.FitToCurve = false;
+ drawingAttributes.FitToCurve = false;
+ }
+
+ SaveSettingsToFile();
+ }
+
+ private void AdvancedSmoothingStrengthSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) {
+ if (!isLoaded) return;
+ Settings.Canvas.AdvancedSmoothingStrength = AdvancedSmoothingStrengthSlider.Value;
+ SaveSettingsToFile();
+ }
+
+ private void AdvancedSmoothingTensionSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e) {
+ if (!isLoaded) return;
+ Settings.Canvas.AdvancedSmoothingTension = AdvancedSmoothingTensionSlider.Value;
+ SaveSettingsToFile();
+ }
+
+ private void ToggleSwitchEnableAdaptiveSmoothing_Toggled(object sender, RoutedEventArgs e) {
+ if (!isLoaded) return;
+ Settings.Canvas.EnableAdaptiveSmoothing = ToggleSwitchEnableAdaptiveSmoothing.IsOn;
SaveSettingsToFile();
}
@@ -1588,7 +1629,11 @@ namespace Ink_Canvas {
Settings.Canvas.EraserShapeType = 1;
Settings.Canvas.HideStrokeWhenSelecting = false;
Settings.Canvas.ClearCanvasAndClearTimeMachine = false;
- Settings.Canvas.FitToCurve = true;
+ Settings.Canvas.FitToCurve = false;
+ Settings.Canvas.UseAdvancedBezierSmoothing = true;
+ Settings.Canvas.AdvancedSmoothingStrength = 0.6;
+ Settings.Canvas.AdvancedSmoothingTension = 0.5;
+ Settings.Canvas.EnableAdaptiveSmoothing = true;
Settings.Canvas.EnablePressureTouchMode = false;
Settings.Canvas.DisablePressure = false;
Settings.Canvas.AutoStraightenLine = true;
diff --git a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
index cf2531a4..e1dd2960 100644
--- a/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
+++ b/Ink Canvas/MainWindow_cs/MW_SettingsToLoad.cs
@@ -515,13 +515,31 @@ namespace Ink_Canvas {
ToggleSwitchHideStrokeWhenSelecting.IsOn = Settings.Canvas.HideStrokeWhenSelecting;
- if (Settings.Canvas.FitToCurve) {
- ToggleSwitchFitToCurve.IsOn = true;
- drawingAttributes.FitToCurve = true;
- } else {
+ // 初始化贝塞尔曲线平滑设置
+ if (Settings.Canvas.UseAdvancedBezierSmoothing)
+ {
+ // 如果启用高级贝塞尔平滑,则禁用原来的FitToCurve
+ ToggleSwitchAdvancedBezierSmoothing.IsOn = true;
ToggleSwitchFitToCurve.IsOn = false;
drawingAttributes.FitToCurve = false;
}
+ else if (Settings.Canvas.FitToCurve)
+ {
+ // 如果启用原来的FitToCurve,则禁用高级贝塞尔平滑
+ ToggleSwitchFitToCurve.IsOn = true;
+ ToggleSwitchAdvancedBezierSmoothing.IsOn = false;
+ drawingAttributes.FitToCurve = true;
+ }
+ else
+ {
+ // 两者都禁用
+ ToggleSwitchFitToCurve.IsOn = false;
+ ToggleSwitchAdvancedBezierSmoothing.IsOn = false;
+ drawingAttributes.FitToCurve = false;
+ }
+ AdvancedSmoothingStrengthSlider.Value = Settings.Canvas.AdvancedSmoothingStrength;
+ AdvancedSmoothingTensionSlider.Value = Settings.Canvas.AdvancedSmoothingTension;
+ ToggleSwitchEnableAdaptiveSmoothing.IsOn = Settings.Canvas.EnableAdaptiveSmoothing;
// 初始化直线自动拉直相关设置
ToggleSwitchAutoStraightenLine.IsOn = Settings.Canvas.AutoStraightenLine;
diff --git a/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs b/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs
index 35aba96f..46037236 100644
--- a/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs
+++ b/Ink Canvas/MainWindow_cs/MW_ShapeDrawing.cs
@@ -433,6 +433,7 @@ namespace Ink_Canvas {
#region 形状绘制主函数
private void MouseTouchMove(Point endP) {
+ // 禁用原有的FitToCurve,使用新的高级贝塞尔曲线平滑
if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = false;
ViewboxFloatingBar.IsHitTestVisible = false;
BlackboardUIGridForInkReplay.IsHitTestVisible = false;
@@ -1589,7 +1590,36 @@ namespace Ink_Canvas {
}
}
- if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = true;
+ // 应用高级贝塞尔曲线平滑
+ if (Settings.Canvas.UseAdvancedBezierSmoothing)
+ {
+ try
+ {
+ var advancedSmoothing = new Helpers.AdvancedBezierSmoothing
+ {
+ SmoothingStrength = Settings.Canvas.AdvancedSmoothingStrength,
+ Tension = Settings.Canvas.AdvancedSmoothingTension,
+ EnableAdaptiveSmoothing = Settings.Canvas.EnableAdaptiveSmoothing
+ };
+
+ // 对临时笔画应用平滑
+ if (lastTempStroke != null)
+ {
+ var smoothedStroke = advancedSmoothing.SmoothStroke(lastTempStroke);
+ inkCanvas.Strokes.Remove(lastTempStroke);
+ lastTempStroke = smoothedStroke;
+ inkCanvas.Strokes.Add(smoothedStroke);
+ }
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"形状绘制高级贝塞尔曲线平滑失败: {ex.Message}");
+ }
+ }
+ else if (Settings.Canvas.FitToCurve == true)
+ {
+ drawingAttributes.FitToCurve = true;
+ }
}
private bool NeedUpdateIniP() {
diff --git a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs
index f66ed808..9cc49768 100644
--- a/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs
+++ b/Ink Canvas/MainWindow_cs/MW_SimulatePressure&InkToShape.cs
@@ -16,6 +16,7 @@ namespace Ink_Canvas {
private const double LINE_STRAIGHTEN_THRESHOLD = 0.20; // 默认灵敏度阈值,与UI默认值对应
private void inkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e) {
+ // 禁用原有的FitToCurve,使用新的高级贝塞尔曲线平滑
if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = false;
try {
@@ -572,7 +573,37 @@ namespace Ink_Canvas {
}
catch { }
- if (Settings.Canvas.FitToCurve == true) drawingAttributes.FitToCurve = true;
+ // 应用高级贝塞尔曲线平滑
+ if (Settings.Canvas.UseAdvancedBezierSmoothing)
+ {
+ try
+ {
+ var advancedSmoothing = new Helpers.AdvancedBezierSmoothing
+ {
+ SmoothingStrength = Settings.Canvas.AdvancedSmoothingStrength,
+ Tension = Settings.Canvas.AdvancedSmoothingTension,
+ EnableAdaptiveSmoothing = Settings.Canvas.EnableAdaptiveSmoothing
+ };
+
+ var smoothedStroke = advancedSmoothing.SmoothStroke(e.Stroke);
+
+ // 替换原始笔画
+ SetNewBackupOfStroke();
+ _currentCommitType = CommitReason.ShapeRecognition;
+ inkCanvas.Strokes.Remove(e.Stroke);
+ inkCanvas.Strokes.Add(smoothedStroke);
+ _currentCommitType = CommitReason.UserInput;
+ }
+ catch (Exception ex)
+ {
+ // 如果高级平滑失败,回退到原始笔画
+ System.Diagnostics.Debug.WriteLine($"高级贝塞尔曲线平滑失败: {ex.Message}");
+ }
+ }
+ else if (Settings.Canvas.FitToCurve == true)
+ {
+ drawingAttributes.FitToCurve = true;
+ }
}
// New method: Checks if a stroke is potentially a straight line
diff --git a/Ink Canvas/Resources/Settings.cs b/Ink Canvas/Resources/Settings.cs
index da490d31..01d44acb 100644
--- a/Ink Canvas/Resources/Settings.cs
+++ b/Ink Canvas/Resources/Settings.cs
@@ -47,7 +47,15 @@ namespace Ink_Canvas
[JsonProperty("hideStrokeWhenSelecting")]
public bool HideStrokeWhenSelecting { get; set; } = true;
[JsonProperty("fitToCurve")]
- public bool FitToCurve { get; set; } = true;
+ public bool FitToCurve { get; set; } = false; // 默认关闭原来的贝塞尔平滑
+ [JsonProperty("useAdvancedBezierSmoothing")]
+ public bool UseAdvancedBezierSmoothing { get; set; } = true; // 默认启用高级贝塞尔曲线平滑
+ [JsonProperty("advancedSmoothingStrength")]
+ public double AdvancedSmoothingStrength { get; set; } = 0.6; // 高级平滑强度 (0.0 - 1.0)
+ [JsonProperty("advancedSmoothingTension")]
+ public double AdvancedSmoothingTension { get; set; } = 0.5; // 高级平滑张力 (0.0 - 1.0)
+ [JsonProperty("enableAdaptiveSmoothing")]
+ public bool EnableAdaptiveSmoothing { get; set; } = true; // 是否启用自适应平滑
[JsonProperty("clearCanvasAndClearTimeMachine")]
public bool ClearCanvasAndClearTimeMachine { get; set; } = false;
[JsonProperty("enablePressureTouchMode")]