0

我有一组控制点,我正在尝试根据这些控制点绘制三次 B 样条曲线(3 阶)。我遇到的问题是我的曲线没有连接到最终控制点,而是将曲线绘制到一起位于不同区域的其他点。曲线点(0, 0)在一定时间后接近。

仅控制点的图像

曲线和控制点的图像。请注意,曲线正确地从第一个控制点开始,但没有在最后一个控制点结束。

我正在使用的代码:

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;
}
4

1 回答 1

0

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

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

您应该检查Spline该类是否区分控制点和节点。如果不是,它很可能只是期望每第三个点(从第一个点开始,然后跳过两个点)成为一个结。在这种情况下,请确保至少添加 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 } );

更好的解决方案是在第 1 点附近反映第 2 点:

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

于 2018-11-30T21:04:52.687 回答