【问题标题】:How do I get the B-spline curve to connect to the final control point?如何让 B 样条曲线连接到最终控制点?
【发布时间】:2018-12-08 14:46:57
【问题描述】:

我有一组控制点,我正在尝试根据这些控制点绘制三次 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;
}

【问题讨论】:

    标签: c++ spline bspline cubic-spline


    【解决方案1】:

    三次 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 } );

    【讨论】:

    • 我认为我的错误来自对结的误解。结是如何计算的?我认为结与控制点不同,因此在我的Spline 课程中,两者之间存在区别。但是,我认为我不会在任何地方计算结。我确实创建了一个节点向量(我不太了解它的目的),但我认为我没有明确计算曲线的节点,这可能是我的错误出现的地方?
    • 可能是“结”和“控制点”可以互换使用,它们之间没有真正的区别。但我通常会做出这样的区分:曲线会通过节点,但不会通过控制点。它只会转向控制点的方向。请参阅this link 玩控制点以获得更好的理解。您自己提供结,如果需要,随机提供。控制点也可以提供,也可以根据节点自动计算。
    • 非常感谢!!看来我的理解是正确的,但是我在节点向量中创建了一个额外的值(
    • 实际上,在使用链接中的那个工具之后,我认为他们会自动计算“结”(正好在两个控制点之间的中间),从而增加了混乱。但我很高兴你明白了。
    • 将一条样条线的终点作为下一条样条线的起点暗示了连续性,不是吗?使相邻控制点与节点共线将使曲线平滑/可微。
    猜你喜欢
    • 2014-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-04
    • 2014-07-16
    • 1970-01-01
    • 2021-08-06
    相关资源
    最近更新 更多