cocos2dx CardinalSpline和CatmullRom算法

想想那些鱼儿游动的漂亮曲线,还记得水果忍者的手指划过那一道道划痕吗,众所周知这一切都是很多个点组成的线段,这些都离不开样条插值算法。


特别注意:CardinalSpline和CatmullRom这两种算法都是过点式,就是形成的曲线一定经过样品点,但是贝塞尔曲线就不一定经过所有的样品点


cocos2dx中主要有两处用到了:

一个是Action下面类:points代表样品点集合,tension代表张力因子,实际效果是如果等于1就是画直直的线,默认等于0.5,就是比较平滑的线,一般去0到1直之间调节

CardinalSplineBy::create(float duration, cocos2d::PointArray *points, float tension)

CardinalSplineTo::create(float duration, cocos2d::PointArray *points, float tension)

CatmullRomTo::create(float duration, cocos2d::PointArray *points)//等效于上面的tension=0.5

CatmullRomBy::create(float duration, cocos2d::PointArray *points)//等效于上面的tension=0.5


一个是DrawNode类下面成员函数,用来画图segments代表共用几个点来形成这条曲线

void drawCardinalSpline(PointArray *points, float tension,  unsigned int segments, const Color4F &color);

void drawCatmullRom(PointArray *points, unsigned int segments, const Color4F &color);


cocos2dx中核心源码:

注解:

这里面lt特别不好理解,实际上这里是吧segments个点平均分成points总数个部分,每个segment的点占自己部分的百分比就是这个lt的意思,

这里面p又是什么意思呢,实际上他就表示区间的序号,在同一个区间内p是一样的。

所以此算法大概的原理就是把需要生成的segments点按样品点总数平均分一下,在固定区间内,找到固定的4个实际样品点,然后在这个区间内按点所在这个区间比例位置逐

个使用ccCardinalSplineAt算法生成新点,是一种典型分治法。不过这些仅仅是程序理解,关于样条插值算法推导过程估计还得找找大学专业的书本了。



void DrawNode::drawCardinalSpline(PointArray *config, float tension,  unsigned int segments, const Color4F &color)
{
    Vec2* vertices = new (std::nothrow) Vec2[segments + 1];
    if( ! vertices )
        return;
    
    ssize_t p;
    float lt;
    float deltaT = 1.0f / config->count();
    
    for( unsigned int i=0; i < segments+1;i++) {
        
        float dt = (float)i / segments;
        
        // border
        if( dt == 1 ) {
            p = config->count() - 1;
            lt = 1;
        } else {
            p = dt / deltaT;
            lt = (dt - deltaT * (float)p) / deltaT;
        }
        
        // Interpolate
        Vec2 pp0 = config->getControlPointAtIndex(p-1);
        Vec2 pp1 = config->getControlPointAtIndex(p+0);
        Vec2 pp2 = config->getControlPointAtIndex(p+1);
        Vec2 pp3 = config->getControlPointAtIndex(p+2);
        
        Vec2 newPos = ccCardinalSplineAt( pp0, pp1, pp2, pp3, tension, lt);
        vertices[i].x = newPos.x;
        vertices[i].y = newPos.y;
    }
    
    drawPoly(vertices, segments+1, false, color);
    
    CC_SAFE_DELETE_ARRAY(vertices);
}


Vec2 ccCardinalSplineAt(Vec2 &p0, Vec2 &p1, Vec2 &p2, Vec2 &p3, float tension, float t)
{
    float t2 = t * t;
    float t3 = t2 * t;
    
    /*
     * Formula: s(-ttt + 2tt - t)P1 + s(-ttt + tt)P2 + (2ttt - 3tt + 1)P2 + s(ttt - 2tt + t)P3 + (-2ttt + 3tt)P3 + s(ttt - tt)P4
     */
    float s = (1 - tension) / 2;
	
    float b1 = s * ((-t3 + (2 * t2)) - t);                      // s(-t3 + 2 t2 - t)P1
    float b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1);          // s(-t3 + t2)P2 + (2 t3 - 3 t2 + 1)P2
    float b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2);      // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3
    float b4 = s * (t3 - t2);                                   // s(t3 - t2)P4
    
    float x = (p0.x*b1 + p1.x*b2 + p2.x*b3 + p3.x*b4);
    float y = (p0.y*b1 + p1.y*b2 + p2.y*b3 + p3.y*b4);
	
    return Vec2(x,y);
}


这里通过cocos2dx源码,小小总结一下CatmullRom算法公式:

static Vec2 CatmullRom(Vec2 &p0, Vec2 &p1, Vec2 &p2, Vec2 &p3, float t)
{
	float t2 = t * t;
	float t3 = t * t * t;
	float s =0.5;
	float b1 = s * ((-t3 + (2 * t2)) - t);                     
	float b2 = s * (-t3 + t2) + (2 * t3 - 3 * t2 + 1);          
	float b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2);     
	float b4 = s * (t3 - t2);                                   
	return p0*b1 + p1*b2 + p2*b3 + p3*b4;
}

static std::vector<Vec2> GetCatmullRom(PointArray *config,unsigned int segments)
{
	std::vector<Vec2> vector;
	ssize_t p;
	float lt;
	float deltaT = 1.0f / config->count();

	for( unsigned int i=0; i < segments+1;i++) {

		float dt = (float)i / segments;

		// border
		if( dt == 1 ) {
			p = config->count() - 1;
			lt = 1;
		} else {
			p = dt / deltaT;
			lt = (dt - deltaT * (float)p) / deltaT;
		}
		// Interpolate
		Vec2 pp0 = config->getControlPointAtIndex(p-1);
		Vec2 pp1 = config->getControlPointAtIndex(p+0);
		Vec2 pp2 = config->getControlPointAtIndex(p+1);
		Vec2 pp3 = config->getControlPointAtIndex(p+2);

		Vec2 newPos = CatmullRom( pp0, pp1, pp2, pp3, lt);
		vector.push_back(newPos);
	}
	return vector;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值