Chaikin Curve(球面插值)

200 篇文章 0 订阅
101 篇文章 3 订阅

http://www.cnblogs.com/hongru/archive/2011/10/27/2226946.html

 在两条折线间完成平滑的过渡是 用画布做UI 或者做类似地图编辑器一类的工作的 很常见的任务。

怎么样化方为圆是决定工作效率的很重要的因素。(当需要编辑的曲线多起来, 复杂起来的时候,这会是件相当繁重的工作)

最容易想到的莫非是 贝塞尔曲线,而且时下几乎所有主流的数学算法库或者画布api 都已经很好的支持了贝塞尔曲线的绘制。 并能提供很便利的接口,通常只需知道 开始位置, 结束位置 ,以及贝塞尔控制点 就可生成一条贝塞尔曲线。

例如:

context.bezierCurveTo(controlX1, controlY1, controlX2, controlY2, endX, endY); 

贝塞尔曲线的定义是由当前的上下文来看,两个控制点,和结束点。曲线的第一部分是切向的假想线,是由上下文点和第一个控制点定义。第二部分的曲线相切的假想线,是第二个控制点和结束点定义。

但是贝塞尔曲线仍旧属于“高输入”api, 尤其是贝塞尔控制点的定义,其实并不是一个简单就能得到的东西。
而且在实际应用中,通常的需求多为 已知折线,然后把折线转化为曲线的情况。

一般来说,折线我们是最好获得的,得到转折点连接起来就可以了。
但是当折线多起来的时候,会发现按照贝塞尔曲线绘制的思路来绘制平滑的曲线会是一件相当困难的事,光是控制点的确定就是一个大工程。

为了更好的,直接的把折线转化为平滑的曲线。我们还是得回到曲线绘制的根源--- 折线模拟

这就是今天想记录的Chaikin Curve, 插值曲线。

复制代码
原理:
我们称一条折线的起始和结束的点 为断点, 中间的转折点都叫折点。
于是, 在每个非端点的折点附近取连接这个折点的两条线段各自的1/3位置处新建一个点。并将这两个新建的点连接。
如此重复下去,直到模拟出想要的平滑度。

根据想要的不同的曲线弧度,也可将1/3 替换成 1/4 或1/2 ...
复制代码

这种方法如此简单,已知一条折线,根据需要定义迭代次数即可得到一条曲线(模拟)。

同时,用这种方式创建的折线还有一个优势,就是很方便的能得到曲线的长度,因为是折线模拟的,所以折线长度之和即为曲线的长度。

也不用担心这种类似二分的迭代方式计算量会有多大。我自己的测试,在800*800的区域通过插值来模拟曲线,基本也就迭代4次就已经足够平滑了。

曲线模拟的过程大致如下的动画:

 

以上都为随机6个点,迭代模拟4次的过程。

主函数即为下面的 subDivide

复制代码
    var Point2 = La.geometry.Point2, // Point Class
        Vector2 = La.geometry.Vector2, // Vector Class
        self = this;
        
    this.subDivide = function (handles, subdivs) {
        if (handles.length) {
            do {
                var numHandles = handles.length;
                // 第一个点
                handles.push(new Point2(handles[0].x, handles[0].y));
 
                for (var i = 0; i < numHandles - 1; ++i) {
                    // 每次拿出两个点
                    var p0 = handles[i];
                    var p1 = handles[i + 1];

                    // 根据两个原始点创建两个新点,做插值
                    var Q = new Point2(0.75 * p0.x + 0.25 * p1.x, 0.75 * p0.y + 0.25 * p1.y);
                    var R = new Point2(0.25 * p0.x + 0.75 * p1.x, 0.25 * p0.y + 0.75 * p1.y);
     
                    handles.push(Q);
                    handles.push(R);
                }
                // 最后一个店
                handles.push(new Point2(handles[numHandles - 1].x, handles[numHandles - 1].y));
 
                // 更新数组
                for (var i = 0; i < numHandles; ++i)
                    handles.shift();
                //handles.shift(numHandles);
            } while (--subdivs > 0);
        }
    };
复制代码


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值