Bspline CatmullRoom and Bezier 样条曲线生成 N 阶贝塞尔曲线生成

此项目旨在生成常见的样条曲线,项目inspired by 叶飞影

首先是各种曲线的基类

#ifndef __SPLINE_BASE__
#define __SPLINE_BASE__

#include <cstddef>

class SplineBase
{
public:
    // 计算样条数值
    virtual bool BuildSpline(const void* ctrlValuesPtr, int ctrlStride, int ctrlCount, void* splineValuesPtr, int splineStride) const = NULL;

    // 计算切线数值
    virtual bool BuildTangent(const void* ctrlValuesPtr, int ctrlStride, int ctrlCount, void* tangentValuesPtr, int tangentStride) const
    {
        return false;
    }
};

inline float YfGetFloatValue(const void* valuesPtr, int stride, int index)
{
    return *(float*)((char*)valuesPtr + stride * index);
}

#endif //__SPLINE_BASE__

每种曲线的类都会继承此基类,并实现 BuildSpline或者同时实现BuildSpline和Buildtangent两个函数。

N阶Bezier 曲线

1. 原理参考我的另外一篇博客:N 阶贝塞尔曲线生成,此篇代码每次都要计算N次方,比较耗时,可以在绘制图形之前,先计算好各阶数值即可。如下代码分别是bezier.h 和 bezier.cpp实现N阶贝塞尔曲线

/****************************************************************

  File name   :  bezier.h
  Author      :  Shike
  Version     :  1.0
  Create Date :  2020/05/01
  Description :  Bezier Spline

*****************************************************************/

#ifndef __BEZIER__SPLINE__
#define __BEZIER__SPLINE__

#include "spline_base.h"

#define YD_MAX_BEZIER_CONTROL_VALUE 33

class Bezier : public SplineBase
{
public:
    Bezier();
    ~Bezier();

    // 设置输出样条值的数目
    void SetSplineValuesCount(int count);

    // 获得输出样条值的数目
    int GetSplineValuesCount() const;

    // 计算样条数值
    bool BuildSpline(const void* ctrlValuesPtr, int ctrlStride, int ctrlCount, void* splineValuesPtr, int splineStride) const;

protected:
    void ClearPowT();
    void BuildPowT();
    
    double GetValueT(int t, int p) const
    {
        return m_pow_t[YD_MAX_BEZIER_CONTROL_VALUE*t + p];
    }

protected:
    int    m_valuesCount;
    double *m_pow_t;

protected:
    static void BuildYanghuiTriangle();

    //pascal's triangle
    static int  m_yanghuiRowIndex[YD_MAX_BEZIER_CONTROL_VALUE];
    static int  m_yanghuiTriangle[(YD_MAX_BEZIER_CONTROL_VALUE + 1)*YD_MAX_BEZIER_CONTROL_VALUE / 2];

};

#endif // __BEZIER__SPLINE__

bezier.cpp

#include "bezier.h"
#include "assert.h"
#include "stdlib.h"

#include <cstdio>

int Bezier::m_yanghuiRowIndex[YD_MAX_BEZIER_CONTROL_VALUE] = { 0 };
int Bezier::m_yanghuiTriangle[(YD_MAX_BEZIER_CONTROL_VALUE + 1)*YD_MAX_BEZIER_CONTROL_VALUE / 2] = { 0 };

Bezier::Bezier()
{
    if (m_yanghuiTriangle[0] == 0)
    {
        BuildYanghuiTriangle();
    }

    m_valuesCount = 0;
    m_pow_t = NULL;

    SetSplineValuesCount(100);
}

Bezier::~Bezier()
{
    ClearPowT();
}

void Bezier::BuildYanghuiTriangle()
{
    // 第0行
    m_yanghuiRowIndex[0] = 0;
    m_yanghuiTriangle[0] = 1;

    int index = 1;
    int t0, t1;
    int* lastRow;

    for (int i = 1; i < YD_MAX_BEZIER_CONTROL_VALUE; i++)
    {
        m_yanghuiRowIndex[i] = index;
        m_yanghuiTriangle[index] = 1;
        index++;

        for (int j = 1; j <= i; j++)
        {
            lastRow = m_yanghuiTriangle + m_yanghuiRowIndex[i - 1];
            t0 = lastRow[j - 1];
            t1 = (j < i) ? lastRow[j] : 0;

            m_yanghuiTriangle[index] = t0 + t1;
            index++;
        }
    }

    assert(index == (YD_MAX_BEZIER_CONTROL_VALUE + 1)*YD_MAX_BEZIER_CONTROL_VALUE / 2);
}

//设置输出样条值的数目
void Bezier::SetSplineValuesCount(int count)
{
    if (count < 2)
    {
        count = 2;
    }

    if (count == m_valuesCount)
    {
        return;
    }

    m_valuesCount = count;
    BuildPowT();
}

//获得输出样条值的数目
int Bezier::GetSplineValuesCount() const
{
    return m_valuesCount;
}

void Bezier::ClearPowT()
{
    if (m_pow_t)
    {
        free(m_pow_t);
        m_pow_t = NULL;
    }
}

void Bezier::BuildPowT()
{
    ClearPowT();

    m_pow_t = (double*)malloc(m_valuesCount*YD_MAX_BEZIER_CONTROL_VALUE * sizeof(double));
    double t;
    for (int i = 0; i < m_valuesCount; i++)
    {
        t = i / (m_valuesCount - 1.0f);

        m_pow_t[i*YD_MAX_BEZIER_CONTROL_VALUE] = 1.0f;
        for (int j = 1; j < YD_MAX_BEZIER_CONTROL_VALUE; j++)
        {
            m_pow_t[i*YD_MAX_BEZIER_CONTROL_VALUE + j] = m_pow_t[i*YD_MAX_BEZIER_CONTROL_VALUE + j - 1] * t;
        }
    }
}

//计算样条数值
bool Bezier::BuildSpline(const void* ctrlValuesPtr, int ctrlStride, int ctrlCount, void* splineValuesPtr, int splineStride) const
{
    if (ctrlCount < 2 || ctrlCount > YD_MAX_BEZIER_CONTROL_VALUE)
    {
        return false;
    }

    double* destValue;
    double* srcValue;
    double v;
    const int* yanghuiRow = m_yanghuiTriangle + m_yanghuiRowIndex[ctrlCount - 1];

    for (int i = 0; i < m_valuesCount; i++)
    {
        for (int dim = 0; dim < ctrlStride; dim++)
        {
            v = 0.0f;
            for (int j = 0; j < ctrlCount; j++)
            {
                srcValue = (double*)ctrlValuesPtr + ctrlStride * j + dim;
                v += yanghuiRow[j] * (*srcValue) * GetValueT(i, j) * GetValueT(m_valuesCount - 1 - i, ctrlCount - 1 - j);
            }

            destValue = (double*)splineValuesPtr + splineStride * i + dim;
            *destValue = v;
        }
    }

    return true;
}

显示效果如下

猜你喜欢

转载自www.cnblogs.com/flyinggod/p/12825835.html