feat(docstring):添加部分docstring (#376)

* feat(docstring):添加docstring

Signed-off-by: doudou0720 <98651603+doudou0720@users.noreply.github.com>

* fix(docstring):修复部分docstring格式错误

Signed-off-by: doudou0720 <98651603+doudou0720@users.noreply.github.com>

* fix(docstring):修复部分docstring

Signed-off-by: doudou0720 <98651603+doudou0720@users.noreply.github.com>

* chore(Docstring):MW_* 前14

* chore(Docstring):MW_* part 2

* chore(Docstring):MW_* part 3

* chore:优化缩进

* fix: 修复数学计算中的潜在除零错误和数值稳定性问题

Signed-off-by: doudou0720 <98651603+doudou0720@users.noreply.github.com>

* chore:删除Rebase时多余的OOBE函数

* chore: 更新代码注释和文档格式

修复XML文档注释中的格式问题,统一使用<c>和<see>标签
更新ConfigHelper类的预留说明,明确未来扩展用途
优化TimerDisplayDate_Elapsed方法的注释,说明UI异步更新机制
合并重复的注释摘要行,提高文档可读性
添加形状识别功能的64位进程限制说明
修正视频呈现器设备选择逻辑的文档说明

* chore(IPPTLinkManager): 更新TryEndSlideShow方法的XML注释格式

* chore: 修正代码注释中的术语和格式问题

更新多个文件中的XML注释,统一使用<see langword="..."/>标记代替<c>...</c>标记
规范术语使用(如"延迟初始化"代替"懒惰初始化")
修正注释中的格式错误和补充说明
调整代码区域的注释对齐格式

---------

Signed-off-by: doudou0720 <98651603+doudou0720@users.noreply.github.com>
This commit is contained in:
doudou0720
2026-02-22 10:14:12 +08:00
committed by GitHub
parent 3e3db27296
commit 656863a7d0
48 changed files with 6220 additions and 192 deletions
@@ -14,26 +14,94 @@ using Point = System.Windows.Point;
namespace Ink_Canvas
{
/// <summary>
/// 主窗口类的部分类,包含压感模拟和墨水到形状识别的功能
/// </summary>
/// <remarks>
/// 本文件主要包含以下功能:
/// 1. 压感模拟:根据输入设备类型和设置模拟不同的压感效果
/// 2. 墨水到形状识别:将手绘墨迹转换为规则形状(直线、圆形、椭圆、三角形、矩形等)
/// 3. 直线自动拉直:将近似直线的墨迹自动拉成直线
/// 4. 端点吸附:将直线端点吸附到其他直线的端点
/// 5. 矩形参考线系统:通过多条直线构成矩形
/// 6. 高级贝塞尔曲线平滑:对墨迹进行平滑处理
/// 7. 异步墨水处理:提高性能的异步墨水处理机制
/// </remarks>
public partial class MainWindow : Window
{
/// <summary>
/// 存储新的笔画集合,用于形状识别
/// </summary>
private StrokeCollection newStrokes = new StrokeCollection();
private List<Circle> circles = new List<Circle>();
private const double LINE_STRAIGHTEN_THRESHOLD = 0.20;
/// <summary>
/// 存储圆形形状的列表
/// </summary>
private List<Circle> circles = new List<Circle>();
/// <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;
@@ -53,6 +121,27 @@ namespace Ink_Canvas
}
}
/// <summary>
/// 处理墨水画布的笔画收集事件
/// </summary>
/// <param name="sender">事件发送者</param>
/// <param name="e">笔画收集事件参数</param>
/// <remarks>
/// 当用户在墨水画布上完成一笔绘制后:
/// 1. 检查是否启用墨迹渐隐功能,如果启用则添加到墨迹渐隐管理器
/// 2. 根据设置处理压感:
/// - 如果禁用压感,统一压感值为0.5
/// - 如果启用触摸压感模式,根据速度模拟压感
/// 3. 如果启用了形状识别:
/// - 检查是否启用了直线自动拉直功能,如果是则尝试拉直线条
/// - 处理形状识别,包括圆形、椭圆、三角形、矩形等
/// 4. 检查是否是压感笔书写,如果是则返回
/// 5. 根据墨水风格设置模拟压感
/// 6. 应用高级贝塞尔曲线平滑(仅在未进行直线拉直时)
/// <para>
/// 注意:形状识别(圆形、椭圆、三角形、矩形等)仅在32位进程中可用。当 Environment.Is64BitProcess 为 true 时,形状识别功能会被禁用。
/// </para>
/// </remarks>
private void inkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
// 检查是否启用墨迹渐隐功能
@@ -775,6 +864,15 @@ namespace Ink_Canvas
/// <summary>
/// 异步处理笔画平滑
/// </summary>
/// <param name="originalStroke">原始笔画</param>
/// <returns>返回一个表示异步操作的Task</returns>
/// <remarks>
/// 异步处理笔画平滑的流程:
/// 1. 调用墨迹平滑管理器的SmoothStrokeAsync方法
/// 2. 在平滑完成后,在UI线程上执行笔画替换
/// 3. 如果原始笔画仍然存在于画布中且平滑后的笔画不同,则替换原始笔画
/// 4. 捕获并记录可能的异常
/// </remarks>
private async Task ProcessStrokeAsync(Stroke originalStroke)
{
try
@@ -808,7 +906,21 @@ namespace Ink_Canvas
}
}
// New method: Checks if a stroke is potentially a straight line
/// <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)
{
// 确保有足够的点来进行线条分析
@@ -910,6 +1022,15 @@ namespace Ink_Canvas
/// <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;
@@ -950,6 +1071,17 @@ namespace Ink_Canvas
/// <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;
@@ -987,6 +1119,17 @@ namespace Ink_Canvas
/// <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;
@@ -1027,6 +1170,17 @@ namespace Ink_Canvas
/// <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;
@@ -1106,7 +1260,8 @@ namespace Ink_Canvas
// 使用海伦公式计算面积
double s = (a + b + c) / 2;
double area = Math.Sqrt(s * (s - a) * (s - b) * (s - c));
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);
@@ -1128,6 +1283,25 @@ namespace Ink_Canvas
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();
@@ -1188,7 +1362,8 @@ namespace Ink_Canvas
// 计算特征值和特征向量
double trace = covXX + covYY;
double determinant = covXX * covYY - covXY * covXY;
double discriminant = Math.Sqrt(trace * trace - 4 * determinant);
double discriminantSquared = trace * trace - 4 * determinant;
double discriminant = discriminantSquared > 0 ? Math.Sqrt(discriminantSquared) : 0;
double eigenvalue1 = (trace + discriminant) / 2;
double eigenvalue2 = (trace - discriminant) / 2;
@@ -1291,7 +1466,19 @@ namespace Ink_Canvas
return explainedVarianceRatio > threshold;
}
// New method: Determines if a stroke should be straightened into a line
/// <summary>
/// 确定笔画是否应该被拉成直线
/// </summary>
/// <param name="stroke">要分析的笔画</param>
/// <returns>如果笔画应该被拉成直线则返回true,否则返回false</returns>
/// <remarks>
/// 确定笔画是否应该被拉成直线的流程:
/// 1. 计算线条长度和分辨率自适应阈值
/// 2. 如果线条太短,不进行拉直处理
/// 3. 检查线条复杂度,如果是复杂形状,不进行拉直处理
/// 4. 尝试获取直线端点,判断是否满足直线条件
/// 5. 根据判断结果返回相应的布尔值
/// </remarks>
private bool ShouldStraightenLine(Stroke stroke)
{
// 分辨率自适应阈值
@@ -1332,6 +1519,28 @@ namespace Ink_Canvas
/// <summary>
/// 计算墨迹的直线度评分(0-1,1表示完美直线)
/// </summary>
/// <param name="stroke">要分析的笔画</param>
/// <returns>返回直线度评分,范围为0到11表示完美直线</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;
@@ -1388,6 +1597,24 @@ namespace Ink_Canvas
/// <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;
@@ -1431,7 +1658,23 @@ namespace Ink_Canvas
return directionScore;
}
// New method: Creates a straight line stroke between two points
/// <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();
@@ -1468,8 +1711,21 @@ namespace Ink_Canvas
}
/// <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)
@@ -1504,7 +1760,20 @@ namespace Ink_Canvas
return sampledPoints;
}
// New method: Gets distance from point to a line defined by two points
/// <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
@@ -1523,6 +1792,23 @@ namespace Ink_Canvas
/// </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)
@@ -1575,7 +1861,26 @@ namespace Ink_Canvas
return false;
}
// New method: Attempts to snap endpoints to existing stroke endpoints
/// <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)
@@ -1645,6 +1950,16 @@ namespace Ink_Canvas
return null;
}
/// <summary>
/// 设置新的笔画备份
/// </summary>
/// <remarks>
/// 设置新的笔画备份的流程:
/// 1. 克隆当前墨水画布的笔画集合
/// 2. 获取当前白板索引
/// 3. 如果当前模式为0,则将白板索引设置为0
/// 4. 将克隆的笔画集合存储到strokeCollections中对应索引的位置
/// </remarks>
private void SetNewBackupOfStroke()
{
lastTouchDownStrokeCollection = inkCanvas.Strokes.Clone();
@@ -1654,12 +1969,36 @@ namespace Ink_Canvas
strokeCollections[whiteboardIndex] = lastTouchDownStrokeCollection;
}
/// <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) +
@@ -1671,10 +2010,13 @@ namespace Ink_Canvas
public Point[] FixPointsDirection(Point p1, Point p2)
{
if (Math.Abs(p1.X - p2.X) / Math.Abs(p1.Y - p2.Y) > 8)
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 = Math.Abs(p1.Y - p2.Y) / 2;
var x = deltaY / 2;
if (p1.Y > p2.Y)
{
p1.Y -= x;
@@ -1686,10 +2028,10 @@ namespace Ink_Canvas
p2.Y -= x;
}
}
else if (Math.Abs(p1.Y - p2.Y) / Math.Abs(p1.X - p2.X) > 8)
else if (deltaX < 1e-10 || deltaY / deltaX > 8)
{
//垂直
var x = Math.Abs(p1.X - p2.X) / 2;
var x = deltaX / 2;
if (p1.X > p2.X)
{
p1.X -= x;