cocos2dx CardinalSpline和CatmullRom算法

来源:互联网 发布:苹果电脑音乐制作软件 编辑:程序博客网 时间:2024/06/05 11:58

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


特别注意: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;// borderif( dt == 1 ) {p = config->count() - 1;lt = 1;} else {p = dt / deltaT;lt = (dt - deltaT * (float)p) / deltaT;}// InterpolateVec2 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;}


0 0
原创粉丝点击