Android-多点绘制平滑曲线

来源:互联网 发布:outlook mac 下载 编辑:程序博客网 时间:2024/05/16 11:11

最近遇到个题,给定一系列坐标点,如何把它们绘制成一条平滑的曲线。

1 Catmull-Rom算法绘制曲线

首先来了解一下,样条曲线(Spline Curves),
是指给定一组控制点而得到一条曲线,曲线的大致形状由这些点予以控制,一般可分为插值样条和逼近样条两种,插值样条通常用于数字化绘图或动画的设计,逼近样条一般用来构造物体的表面。

而Catmull-Rom就是其中一种常用于绘制曲线的样条曲线算法,我们可以把它当成是一种特殊的贝塞尔曲线,一种能够经过所有控制点的曲线。

给定四个坐标点,P0,P1,P2,P3,同时再给定一个float值t(从P1移动到P2的同时,t从0变化到1),可以绘制P1到P2这段曲线的坐标点。

我们可以看到相关的计算公式:

这里写图片描述

根据公式我们可以转化成代码:

private PointF interpolatedPosition(PointF point0, PointF point1,                                         PointF point2, PointF point3, float i) {        float u3 = i * i * i;        float u2 = i * i;        float f1 = -0.5f * u3 + u2 - 0.5f * i;        float f2 = 1.5f * u3 - 2.5f * u2 + 1.0f;        float f3 = -1.5f * u3 + 2.0f * u2 + 0.5f * i;        float f4 = 0.5f * u3 - 0.5f * u2;        float x = point0.x * f1 + point1.x * f2 + point2.x * f3 + point3.x * f4;        float y = point0.y * f1 + point1.y * f2 + point2.y * f3 + point3.y * f4;        return new PointF(x, y);    }

给定一系列坐标点:

        mPointFList = new ArrayList<>();        mPointFList.add(new PointF(0, 500));        mPointFList.add(new PointF(100, 330));        mPointFList.add(new PointF(200, 280));        mPointFList.add(new PointF(300, 460));        mPointFList.add(new PointF(400, 560));        mPointFList.add(new PointF(500, 200));        mPointFList.add(new PointF(600, 300));        mPointFList.add(new PointF(700, 340));

效果图:

这里写图片描述

相关参考:
http://www.dxstudio.com/guide_content.aspx?id=70a2b2cf-193e-4019-859c-28210b1da81f

2 三阶贝塞尔曲线绘制

在Android开发中多用二阶或者三阶贝塞尔曲线来绘制曲线,但是大多数情况我们是基于两个点来绘制,当坐标点数量多起来的时候,我们遇到一个难题,如果使用多段贝塞尔曲线来绘制,如何处理两端曲线之间的连接,使得它们过度自然。

感谢郑航,看了他的文章之后,大概了解了如何实现。要使得连续两段的贝塞尔曲线的连接自然,我们需要使得接头处,前后两段曲线的曲线率相同。

如果我们要绘制P(i)到P(i+1)之间的曲线,那么我们就需要知道两个控制点A和B,那么现在根据上面的原理,我们已经可以得到这两个点。

A.x = P(i).x + (P(i + 1).x - P(i - 1).x) * a;
A.y = P(i).y + (P(i + 1).y - P(i - 1).y) * a;

B.x = P(i + 1).x + (P(i + 2).x - P(i).x) * b;
B.y = P(i + 1).y + (P(i + 2).y - P(i).y) * b;

这里的值a和值b可以是任意正数。

我们用java代码来实现:

    /**     * 根据已知点获取第i个控制点的坐标     * @param pointFList     * @param currentIndex     * @param ctrlPointA     * @param ctrlPointB     */    private void getCtrlPoint(List<PointF> pointFList, int currentIndex,                               PointF ctrlPointA, PointF ctrlPointB) {        ctrlPointA.x = pointFList.get(currentIndex).x +                 (pointFList.get(currentIndex + 1).x - pointFList.get(currentIndex - 1).x) * CTRL_VALUE_A;        ctrlPointA.y = pointFList.get(currentIndex).y +                 (pointFList.get(currentIndex + 1).y - pointFList.get(currentIndex - 1).y) * CTRL_VALUE_A;        ctrlPointB.x = pointFList.get(currentIndex + 1).x -                 (pointFList.get(currentIndex + 2).x - pointFList.get(currentIndex).x) * CTRL_VALUE_B;        ctrlPointB.y = pointFList.get(currentIndex + 1).y -                 (pointFList.get(currentIndex + 2).y - pointFList.get(currentIndex).y) * CTRL_VALUE_B;    }

上面我们讲到a和b的值可以是任意正数,但是它们是会影响到最后绘制出来的曲线的效果的。

我们可以对a和b的值取不同值,坐标点依然采用上面例子的数值,然后观察最后绘制出来的曲线图

1)a = b = 0.05f

这里写图片描述

2)a = b = 0.2f

这里写图片描述

3) a = b = 0.5f

这里写图片描述

可以看到,当a和b的值取0.2f的时候,我们通过贝塞尔曲线绘制出来的曲线,是比较平滑的,而且很接近通过样条曲线算法绘制的曲线。

相关参考:
http://www.zheng-hang.com/?id=43


两种方式绘制出来的曲线基本上是一致的,相信其实有更多更有效率的绘制方法,希望能继续补充~

代码demo: https://github.com/82367825/Polyline

原创粉丝点击