GL绘制自定义线条2_手写曲线应用贝塞尔曲线

        上一篇文章的曲线是由触摸点直接生成的,但触摸点并非连续的,而是离散的,而且屏幕触摸点采样的间隔时间其实不短,因此如果单纯只用触摸点生成OpenGL触摸曲线,在高速书写时会导致曲线看起来就像多个线段合起来一样(事实也是如此)。

        为了解决这个问题,最好的办法就是像Path一样使用Bezier曲线,以三次触摸坐标记录为关键点补间出相对更细密的曲线坐标,从而使线段变得更光滑。

        二次贝塞尔曲线的原理:

        假定我现在有3个顶点(P0、P1、P2),想以曲线的方式进行绘制其路径,那么可以从P0到P1、P1到P2两个线段,按照同样的百分比步进量,沿着这两个线段对应百分比的位置连接为一个新的线段(图中绿色部分),然后取这个线段同样百分比位置的坐标,将这些坐标连城线即可得到一端曲线。

GL绘制自定义线条2_手写曲线应用贝塞尔曲线_第1张图片

 

 

推导:

        但现在要面临的是不定数目的线条顶点,如何才能使用二次贝塞尔曲线连成一个光滑的曲线呢?

        首先像下图这样,画完一段曲线后,关键点全部顺移一个序号,肯定是无法收尾相接的,因为两个曲线段之间断开了:

GL绘制自定义线条2_手写曲线应用贝塞尔曲线_第2张图片

        克服办法也不难想到,每段线段起始的位置,都把上一次使用的P0和P1向量相加之后除以2,即可让下一段曲线的起始段和之前曲线的结尾段结合起来了。

        关键代码如下:

        二次贝塞尔关键点的计算:

PointF keyPoint0 = new PointF((mBezierKeyPoint0[0] + mBezierKeyPoint1[0]) / 2f, (mBezierKeyPoint0[1] + mBezierKeyPoint1[1]) / 2f);
PointF keyPoint1 = new PointF(mBezierKeyPoint1[0], mBezierKeyPoint1[1]);
PointF keyPoint2 = new PointF((x + mBezierKeyPoint1[0]) / 2f, (y + mBezierKeyPoint1[1]) / 2f);
List points = bezierCalc(new PointF[] {keyPoint0, keyPoint1, keyPoint2});

        二次贝塞尔曲线点计算方法:

private List bezierCalc(PointF[] keyPointP) {
    List points = new LinkedList<>();
    double t = 0.1f; //步进
    for (double k = 0; k <= 1f; k += t) {
        double r = 1 - k;
        double x = Math.pow(r, 2) * keyPointP[0].x + 2 * k * r * keyPointP[1].x
                + Math.pow(k, 2) * keyPointP[2].x;
        double y = Math.pow(r, 2) * keyPointP[0].y + 2 * k * r * keyPointP[1].y
                + Math.pow(k, 2) * keyPointP[2].y;
        points.add(new PointF((float) x, (float) y));
    }
    return points;
}

        再把上述算法插入到上次的GL线条绘制代码中,在生成线条顶点之前通过以上算法插值平滑一下,最后结果如下:

GL绘制自定义线条2_手写曲线应用贝塞尔曲线_第3张图片

 GL绘制自定义线条2_手写曲线应用贝塞尔曲线_第4张图片

 

你可能感兴趣的:(图像处理,OpenGL,ES,手写,贝塞尔曲线,粗线条)