.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没?