我有一组控制点,并且我试图根据这些控制点绘制三次B样条曲线(3级)。我遇到的问题是我的曲线没有连接到最终控制点,而是将曲线绘制到了位于不同区域中的其他某个点。一定时间后,曲线点会接近(0, 0)
。
Image of just the control points
我正在使用的代码:
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;
}
答案 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 } );