如何获得B样条曲线以连接到最终控制点?

时间:2018-11-30 20:39:45

标签: c++ spline bspline cubic-spline

我有一组控制点,并且我试图根据这些控制点绘制三次B样条曲线(3级)。我遇到的问题是我的曲线没有连接到最终控制点,而是将曲线绘制到了位于不同区域中的其他某个点。一定时间后,曲线点会接近(0, 0)

Image of just the control points

Image of the curve and the control points. Note that the curve correctly starts at the first control point but does not end at the last control point.

我正在使用的代码:

float Stroke::calculate_N(float t, int i, int j, vector<float> knots){
    float t_1 = knots[i];
    float t_2 = knots[(i + j)];
    float t_3 = knots[(i + 1)];
    float t_4 = knots[(i + j + 1)];

    // Base case of basis function
    if (j == 0){
        if (t_1 <= t && t < t_3) return 1;
        else return 0;
    }

    float temp1 = (t_2 - t_1 == 0) ? 0 : ((t - t_1) / (t_2 - t_1)) * calculate_N(t, i, j-1, knots);
    float temp2 = (t_4 - t_3 == 0) ? 0 : ((t_4 - t) / (t_4 - t_3)) * calculate_N(t, i+1, j-1, knots);

    return temp1 + temp2;
}

vector<float> make_knot_vector(int m, int p, int n){
    vector<float> knots;
    for (int i = 0; i <= p; i++){
        knots.push_back(0.0);
    }
    for (int i = 1; i <= n - p; i++){
        knots.push_back((float)i/(float)(n-p+1));
    }
    for (int i = 0; i <= p; i++){
        knots.push_back(1.0);
    }
    return knots;
}

int main(){
    // Init control points
    s = Spline();
    s.add_control_point(100,100);
    s.add_control_point(232,71);
    s.add_control_point(148,294);
    s.add_control_point(310,115);
    s.add_control_point(375,280);

    // Get the number of knots based on the number of control points and degree
    int num_ctrl_pts = s.get_control_points().size();
    float NUM_KNOTS = (float)(num_ctrl_pts + 3 + 1);

    // Draw each control point in red
    for (auto pt : s.get_control_points()){
        int x = pt->get_x();
        int y = pt->get_y();
        int r = s.get_radius();

        vector<vector<float>> circle_points = calc_circ(y, x, r);
        int si = circle_points.size();
        for (auto circ_point : circle_points){
            c->setColor(circ_point[0], circ_point[1], Color(1.0, 0.0, 0.0));
        }
    }

    // Draw the curve
    vector<float> knots = make_knot_vector(NUM_KNOTS, 3, num_ctrl_pts);
    for (float t = 0.0; t < 1.0; t+= 1.0/1000.0){
        Vector sum = Vector(0.0, 0.0);

        for (int i = 0;i < num_ctrl_pts; i++){
            Vector next = *(s.get_control_points()[i]);
            float n = s.calculate_N(t, i, 3, knots);
            next = next * n;
            sum = sum + next;
        }

        cout<<"("<<(int)sum.get_x()<<", "<<(int)sum.get_y()<<")"<<endl;

        // Draw the curve point in green
        vector<vector<float>> circle_points = calc_circ((int)sum.get_y(), (int)sum.get_x(), s.get_radius());
        for (auto circ_point : circle_points){
            c->setColor(circ_point[0], circ_point[1], Color(0.0, 1.0, 0.0));
        }
    }

    c->writeImage(path + "spline.ppm");

    // delete canvas;
    return 0;
}

1 个答案:

答案 0 :(得分:0)

三次B样条曲线由一个起点(结),2个控制点和一个终点(结)组成。曲线不通过控制点,而仅通过结。

将多个三次B样条曲线组合成一个形状时,一个样条曲线的终点通常是下一个样条曲线的起点,以避免产生间隙。为了使曲线看起来平滑,此结点和相邻的控制点必须是共线的(所有三个都在同一条线上)。

您应该检查Spline类是否区分控制点和结点。如果不是这样,很可能它只是希望每个第3点(从第一个点开始,然后跳过两个点)都是一个结。在这种情况下,请确保至少添加4个点,或7、10个点,等等。

如果Spline仅接受打结(在这种情况下,您应将add_control_point()成员函数重命名为add_knot()),它可以自动计算其控制点。一种常见的方法是构造一个Catmull-Rom样条。样条曲线将通过点2、3、4 ... n-1。要添加第一个(从1到2)和最后一个段(从n-1到n),通常将第一个和最后一个点相加两次。

// Pseudo code: auto spline = CatmullRom( { p1, p1, p2, p3, p4, p5, p5 } );

一个更好的解决方案是将第二个点反映在第一个点附近:

auto p0 = 2 * p1 - p2; auto p6 = 2 * p5 - p4; auto spline = CatmullRom( { p0, p1, p2, p3, p4, p5, p6 } );

相关问题