我尝试实现 NURBS 来描述圆弧(此处为圆弧的 1/4)。不幸的是,NURBS 曲线与真实弧线有点远。我期望它会像这里一样更加封闭,例如,https://nurbscalculator.in/或在科学论文中。 我的实现如下图 绿色是NURBS曲线,红色是精确的圆弧。代码如下。也许你会有些想法我错了。我玩了knotVector,但没有成功。
void GLWidget::NURBS()
{
// NURBS test
glPushMatrix();
glLineWidth(5.0);
glColor3f (0.0, 1.0, 0.0);
std::vector<float> knotVector = {0.0f, 0.125f, 0.125f, 0.5f, 0.5f, 1.0f};
// std::vector<float> knotVector = {0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f};
int nknots = knotVector.size();
std::vector<float> controlPoints = {30.0f, 20.0f, 0.0f, 1.0f,
50.0f, 20.0f, 0.0f, 0.707f,
50.0f, 0.0f, 0.0f, 1.0f};
// NURBS degree
int degree = 2;
// define the parameter values along the curve
float tMin = knotVector[degree];
float tMax = knotVector[nknots - degree - 1];
// float tMax = knotVector[nknots - 1];
// number of points
int numPoints = 100;
// step
float dt = (tMax - tMin) / numPoints;
// curve points
QVector<float> curvePoints;
for (float t = tMin; t <= tMax; t += dt)
{
// span calculation
int span = findSpan(knotVector, nknots, degree, t);
//basis calculation
QVector<float> basisFunctions = calculateBasisFunctions(knotVector, span, t, degree);
qDebug() << "basisFunctions"<< basisFunctions;
qDebug() << "span" << span;
// accumulate the control points multiplied by the basis function values
QVector<float> curvePoint(4, 0.0f);
for (int i = 0; i <= degree; ++i)
{
float basis = basisFunctions[i];
for (int j = 0; j < 4; ++j)
{
curvePoint[j] += controlPoints[(span - degree + i) * 4 + j] * basis;
}
}
// add the curve point to the vector
curvePoints += curvePoint;
}
// paint the NURBS curve
glBegin(GL_LINE_STRIP);
for (int i = 0; i < curvePoints.size(); i += 4)
{
glVertex3f(curvePoints[i], curvePoints[i + 1], curvePoints[i + 2]);
}
glEnd();
update();
}
int GLWidget::findSpan(const std::vector<float>& knots, int n, int degree, float t)
{
if (t >= knots[n-1])
return n - degree - 2;
else if (t <= knots[degree])
return degree;
int low = degree;
int high = n;
int mid = (low + high) / 2;
while (t < knots[mid] || t >= knots[mid+1])
{
if (t < knots[mid])
high = mid;
else
low = mid;
mid = (low + high) / 2;
}
return mid;
}
QVector<float> GLWidget::calculateBasisFunctions(const std::vector<float>& knots, int span, float t, int degree)
{
QVector<float> basisFunctions(degree + 1, 0.0f);
QVector<float> left(degree + 1, 0.0f);
QVector<float> right(degree + 1, 0.0f);
basisFunctions[0] = 1.00f;
for (int j = 1; j <= degree; ++j)
{
left[j] = t - knots[span + 1 - j];
right[j] = knots[span + j] - t;
float saved = 0.0f;
for (int r = 0; r < j; ++r)
{
float temp = basisFunctions[r] / (right[r + 1] + left[j - r]);
basisFunctions[r] = saved + right[r + 1] * temp;
saved = left[j - r] * temp;
}
basisFunctions[j] = saved;
}
return basisFunctions;
}
好的,我找到了解决方案。它的工作半径 = 1 单位。之后,插值圆弧可以缩放和移动。