.NET 笔迹擦除8边形

笔迹擦除是白板软件的核心功能,擦除是以固定形状对笔迹进行碰撞测试,如有相关则分割Stroke线条。

擦除区域与橡皮大小不一致

测试反馈,擦除区域与真实的橡皮大小不一致:

 

上图中,橡皮显示是圆形的,但擦除效果是一个“8边形”区域。

找了一台8K屏,确实是能复现的:

看到这个诡异的8边形,一开始我是以为是逗逼小伙伴在手势识别模块写出来的BUG

但开发肯定不会弄这么规整的形状出来,所以还是要看下擦除模块、看下具体问题在哪

擦除模块流程&定位

StrokeCollection类下面,有个GetIncrementalStrokeHitTester方法,根据擦除图形来创建擦除命中测试的处理类

所以我们创建了一个100*100大小的圆形 ,用于擦除操作

1     var stylusShape = new EllipseStylusShape(100,100);
2     var hitTester = InkStrokes.GetIncrementalStrokeHitTester(stylusShape);

既然是Ellipse,那肯定不存在8边形。

我们再看看具体的擦除操作:

 1     private void CreateStrokeHitTester()
 2     {
 3         var stylusShape = new EllipseStylusShape(100, 100);
 4         var hitTester = InkStrokes.GetIncrementalStrokeHitTester(stylusShape);
 5         hitTester.AddPoints(points);
 6         hitTester.StrokeHit += StrokeHitTester;
 7     }
 8     private void StrokeHitTester1(object sender, StrokeHitEventArgs e)
 9     {
10         var hitStroke = e.HitStroke;
11         var eraseResults = e.GetPointEraseResults();
12     }

hitTester.AddPoints(points) -- 为命中测试处理类,添加点集。

hitTester.StrokeHit += StrokeHitTester -- 添加点集时,hitTester内部会根据点集形成一条路径,然后获取路径对应的矩形Bounds。

使用路径Bounds与笔迹Strokes做相交判断,确认相交的话,触发StrokeHit命中事件并生成擦除后的Strokes:

 

可以看到StrokeHitEventArgs构造有俩个参数,一个是HitStroke(擦除操作命中的笔迹),另一个是擦除后的笔迹集合(可能是一个stroke,也可能是多个)

我们回到擦除8边形的问题上。所以,上面擦除操作中的代码并没有所谓的”8边形“相关异常,我们去看看其它的代码

上图中有个参数_erasingStroke,_erasingStroke = new ErasingStroke(eraserShape),而eraserShape就是我们上面说的擦除形状。

我们一步步跟下去,可以看到StrokeNodeOperations初始化时,去调用了StylusShape抽象类的内部方法GetVerticesAsVectors():

而StylusShape内部,如果是矩形形状则直接在构造函数初始化形状的顶点数据。如果是圆形则会延迟初始化,在有需要时获取顶点数据_vertices:

1     Point[] bezierControlPoints = this.GetBezierControlPoints();
2     vertices = new Vector[bezierControlPoints.Length];
3     for (int index = 0; index < vertices.Length; ++index)
4       vertices[index] = (Vector) bezierControlPoints[index];

圆形是通过执行GetBezierControlPoints来获取内部的点集,我们来看GetBezierControlPoints函数:

如上图所标示,顶点一共12个,其实应该叫”12边形“,只不过因为上下左右的折角是180度,所以看起来是8边形。

0.552284749830793这个系数是8边形折线的水平/竖直位置计算系数。

根据这12个点,生成12个向量。然后以12个向量组合为Bounds,这个Bounds就是擦除命中测试的图形。

根据上面描述,我们可以确定,8边形问题源头就是这个StylusShape类,源码设计如此。

至于为何是8边形,而不是其它的形状。应该是不想用太复杂的图形去做计算,毕竟真要实现完整的圆形,数学几何里圆也是多边形,所以要用N多边形的话性能会有影响。

而在一个正方形的基础上,搞四个边角,能实现擦除功能也能保障擦除性能。这个设计没毛病。

以WPF为框架的白板/批注等应用,都有这个BUG。所以,测试同学有给你报这个BUG没?

 

posted @ 2022-08-16 21:53  唐宋元明清2188  阅读(179)  评论(2编辑  收藏  举报