Как получить кривую B-сплайна для соединения с конечной контрольной точкой? - PullRequest
0 голосов
/ 30 ноября 2018

У меня есть набор контрольных точек, и я пытаюсь нарисовать кубический 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;
}

1 Ответ

0 голосов
/ 01 декабря 2018

Кубический 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 } );

Лучшее решениедолжен отразить 2-ю точку вокруг 1-й:

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

...