且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何绘制通过一组用贝塞尔曲线点的流畅的线条?

更新时间:2023-11-24 08:18:52

贝塞尔曲线的设计并不去所提供的分!它们被设计成通过控制点来塑造的平滑曲线的影响的。 此外,您不希望您的光滑的曲线通过所有的数据点!

相反,内插,你应该考虑过滤数据集:

过滤

有关这种情况下,你需要你的数据序列,为点阵列的顺序在手指绘制的姿势:

您应该在***中有关滑动平均。
你应该用一个小的平均窗。 (尝试5 - 10分)。这种工作方式如下:(查找维基更详细的说明)

我在这里使用了10个点的平均窗口: 通过计算点0的平均启动 - 9,并输出结果作为结果点0 然后计算点1的平均 - 10和输出,造成1 等等。

计算N点之间的平均:
avgX =(X0 +​​ X1 .... XN)/ N
avgY =(Y0 + Y1 .... YN)/ N

最后,你用线连接所产生的点。

如果您仍然需要缺少点之间进行插值,那么你就应该用一块 - 明智的三次样条
。 其中三次样条贯穿全部3提供了点。
您将需要计算系列当中。

不过,首先尝试的滑动平均值。这是很容易的。

I need to draw a smooth line through a set of vertices. The set of vertices is compiled by a user dragging their finger across a touch screen, the set tends to be fairly large and the distance between the vertices is fairly small. However, if I simply connect each vertex with a straight line, the result is very rough (not-smooth).

I found solutions to this which use spline interpolation (and/or other things I don't understand) to smooth the line by adding a bunch of additional vertices. These work nicely, but because the list of vertices is already fairly large, increasing it by 10x or so has significant performance implications.

It seems like the smoothing should be accomplishable by using Bezier curves without adding additional vertices.

Below is some code based on the solution here:

http://www.antigrain.com/research/bezier_interpolation/

It works well when the distance between the vertices is large, but doesn't work very well when the vertices are close together.

Any suggestions for a better way to draw a smooth curve through a large set of vertices, without adding additional vertices?

        Vector<PointF> gesture;
        protected void onDraw(Canvas canvas)
        {
            if(gesture.size() > 4 )
            {
                Path gesturePath = new Path();

                gesturePath.moveTo(gesture.get(0).x, gesture.get(0).y);
                gesturePath.lineTo(gesture.get(1).x, gesture.get(1).y);

                for (int i = 2; i < gesture.size() - 1; i++)
                {
                    float[] ctrl = getControlPoint(gesture.get(i), gesture.get(i - 1), gesture.get(i), gesture.get(i + 1));
                    gesturePath.cubicTo(ctrl[0], ctrl[1], ctrl[2], ctrl[3], gesture.get(i).x, gesture.get(i).y);
                }

                gesturePath.lineTo(gesture.get(gesture.size() - 1).x, gesture.get(gesture.size() - 1).y);
                canvas.drawPath(gesturePath, mPaint);
            }
        }
}


    private float[] getControlPoint(PointF p0, PointF p1, PointF p2, PointF p3)
    {
        float x0 = p0.x;
        float x1 = p1.x;
        float x2 = p2.x;
        float x3 = p3.x;
        float y0 = p0.y;
        float y1 = p1.y;
        float y2 = p2.y;
        float y3 = p3.y;

           double xc1 = (x0 + x1) / 2.0;
            double yc1 = (y0 + y1) / 2.0;
            double xc2 = (x1 + x2) / 2.0;
            double yc2 = (y1 + y2) / 2.0;
            double xc3 = (x2 + x3) / 2.0;
            double yc3 = (y2 + y3) / 2.0;

            double len1 = Math.sqrt((x1-x0) * (x1-x0) + (y1-y0) * (y1-y0));
            double len2 = Math.sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1));
            double len3 = Math.sqrt((x3-x2) * (x3-x2) + (y3-y2) * (y3-y2));

            double k1 = len1 / (len1 + len2);
            double k2 = len2 / (len2 + len3);

            double xm1 = xc1 + (xc2 - xc1) * k1;
            double ym1 = yc1 + (yc2 - yc1) * k1;

            double xm2 = xc2 + (xc3 - xc2) * k2;
            double ym2 = yc2 + (yc3 - yc2) * k2;

            // Resulting control points. Here smooth_value is mentioned
            // above coefficient K whose value should be in range [0...1].
            double k = .1;

            float ctrl1_x = (float) (xm1 + (xc2 - xm1) * k + x1 - xm1);
            float ctrl1_y = (float) (ym1 + (yc2 - ym1) * k + y1 - ym1);

            float ctrl2_x = (float) (xm2 + (xc2 - xm2) * k + x2 - xm2);
            float ctrl2_y = (float) (ym2 + (yc2 - ym2) * k + y2 - ym2);

            return new float[]{ctrl1_x, ctrl1_y, ctrl2_x, ctrl2_y};
    }

Bezier Curves are not designed to go through the provided points! They are designed to shape a smooth curve influenced by the control points. Further you don't want to have your smooth curve going through all data points!

Instead of interpolating you should consider filtering your data set:

Filtering

For that case you need a sequence of your data, as array of points, in the order the finger has drawn the gesture:

You should look in wiki for "sliding average".
You should use a small averaging window. (try 5 - 10 points). This works as follows: (look for wiki for a more detailed description)

I use here an average window of 10 points: start by calculation of the average of points 0 - 9, and output the result as result point 0 then calculate the average of point 1 - 10 and output, result 1 And so on.

to calculate the average between N points:
avgX = (x0+ x1 .... xn) / N
avgY = (y0+ y1 .... yn) / N

Finally you connect the resulting points with lines.

If you still need to interpolate between missing points, you should then use piece - wise cubic splines.
One cubic spline goes through all 3 provided points.
You would need to calculate a series of them.

But first try the sliding average. This is very easy.