Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/371.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/201.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用贝塞尔曲线通过一组点绘制平滑线?_Java_Android - Fatal编程技术网

Java 如何使用贝塞尔曲线通过一组点绘制平滑线?

Java 如何使用贝塞尔曲线通过一组点绘制平滑线?,java,android,Java,Android,我需要通过一组顶点画一条平滑的线。顶点集是由用户在触摸屏上拖动手指编辑而成的,该集往往相当大,顶点之间的距离也相当小。但是,如果我简单地用一条直线连接每个顶点,结果会非常粗糙(不是平滑的) 我找到了解决方案,使用样条插值(和/或其他我不懂的东西)通过添加一堆额外的顶点来平滑直线。这些方法工作得很好,但由于顶点列表已经相当大,因此将其增加10倍左右会对性能产生重大影响 看起来平滑应该通过使用贝塞尔曲线而不添加额外的顶点来实现 以下是基于此解决方案的一些代码: 当顶点之间的距离较大时,该方法效果良

我需要通过一组顶点画一条平滑的线。顶点集是由用户在触摸屏上拖动手指编辑而成的,该集往往相当大,顶点之间的距离也相当小。但是,如果我简单地用一条直线连接每个顶点,结果会非常粗糙(不是平滑的)

我找到了解决方案,使用样条插值(和/或其他我不懂的东西)通过添加一堆额外的顶点来平滑直线。这些方法工作得很好,但由于顶点列表已经相当大,因此将其增加10倍左右会对性能产生重大影响

看起来平滑应该通过使用贝塞尔曲线而不添加额外的顶点来实现

以下是基于此解决方案的一些代码:

当顶点之间的距离较大时,该方法效果良好,但当顶点靠近时效果不佳

有什么建议可以更好地通过一大组顶点绘制平滑曲线,而无需添加其他顶点

        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};
    }
矢量手势;
受保护的void onDraw(画布)
{
如果(手势大小()>4)
{
路径gesturePath=新路径();
moveTo(手势.get(0.x,手势.get(0.y));
lineTo(signature.get(1.x,signature.get(1.y));
对于(int i=2;i
这是干什么用的?为什么你需要如此准确?我假设您只需要为用户拖动手指的每英寸存储大约4个顶点。考虑到这一点:

尝试使用每个X中的一个顶点来实际绘制,中间顶点用于指定曲线的加权点

int interval = 10; //how many points to skip
gesture.moveTo(gesture.get(0).x, gesture.get(0).y);
for(int i =0; i +interval/2 < gesture.size(); i+=interval)
{
   Gesture ngp = gesture.get(i+interval/2);
   gesturePath.quadTo(ngp.x,ngp.y, gp.x,gp.y);
}
int间隔=10//要跳过多少分
手势.moveTo(手势.get(0.x),手势.get(0.y);
对于(int i=0;i+interval/2

你需要调整它以使其实际工作,但想法已经存在。

问得好。您的(错误的)结果是显而易见的,但您可以尝试将其应用于更小的数据集,也许可以用平均点替换接近点的组。在这种情况下,判断两个或多个点是否属于同一组的适当距离可以用时间表示,而不是用空间表示,因此您需要存储整个触摸事件(x、y和时间戳)。我之所以想到这一点,是因为我需要一种方法,让用户通过触摸绘制几何图元(矩形、直线和简单曲线)

贝塞尔曲线不是设计用来通过提供的点的!它们的设计目的是形成受控制点影响的平滑曲线。 此外,您不希望平滑曲线通过所有数据点

代替插值,你应该考虑过滤你的数据集:

过滤

在这种情况下,您需要一系列数据,作为点阵列,按照手指绘制手势的顺序:

您应该在wiki中查找“滑动平均值”。
你应该使用一个小的平均窗口。(试试5-10分)。其工作原理如下:(有关更详细的描述,请查看wiki)

我在这里使用10点的平均窗口: 首先计算点0-9的平均值,并将结果输出为结果点0 然后计算点1-10的平均值并输出结果1 等等

要计算N个点之间的平均值:
avgX=(x0+x1…xn)/N
平均值=(y0+y1…yn)/N

最后,用线连接结果点

如果仍然需要在缺失点之间插值,则应使用分段三次样条曲线。
一条三次样条曲线穿过所有3个提供的点。