void ON_TextLog::PrintKnotVector( int order, int cv_count, const double* knot ) { int i, i0, mult, knot_count; if ( !knot ) Print("NULL knot vector\n"); if ( order < 2 ) Print("knot vector order < 2\n"); if ( cv_count < order ) Print("knot vector cv_count < order\n"); if ( order >= 2 && cv_count >= order && knot ) { knot_count = ON_KnotCount( order, cv_count ); i = i0 = 0; Print("index value mult delta\n"); while ( i < knot_count ) { mult = 1; while ( i+mult < knot_count && knot[i] == knot[i+mult] ) mult++; if ( i == 0 ) { Print( "%5d %23.20g %4d\n", i, knot[i], mult ); } else { Print( "%5d %23.20g %4d %10.4g\n", i, knot[i], mult, knot[i]-knot[i0] ); } i0 = i; i += mult; } } }
bool ON_ClampKnotVector( int order, // order (>=2) int cv_count, // cv count double* knot, // knot[] array int end // 0 = clamp start, 1 = clamp end, 2 = clamp both ends ) { // sets initial/final order-2 knot values to match knot[order-2]/knot[cv_count-1] bool rc = false; int i, i0; if ( knot && order >= 2 && cv_count >= order ) { if ( end == 0 || end == 2 ) { i0 = order-2; for ( i = 0; i < i0; i++ ) { knot[i] = knot[i0]; } rc = true; } if ( end == 1 || end == 2 ) { const int knot_count = ON_KnotCount(order,cv_count); i0 = cv_count-1; for ( i = i0+1; i < knot_count; i++ ) { knot[i] = knot[i0]; } rc = true; } } return rc; }
bool ON_SetKnotVectorDomain( int order, int cv_count, double* knot, double t0, double t1 ) { bool rc = false; if ( order < 2 || cv_count < order || 0 == knot || t0 >= t1 || !ON_IsValid(t0) || !ON_IsValid(t1) ) { ON_ERROR("ON_SetKnotVectorDomain - invalid input"); } else if ( knot[order-2] >= knot[cv_count-1] || !ON_IsValid(knot[order-2]) || !ON_IsValid(knot[cv_count-2]) ) { ON_ERROR("ON_SetKnotVectorDomain - invalid input knot vector"); } else { const ON_Interval oldd(knot[order-2],knot[cv_count-1]); const ON_Interval newd(t0,t1); if ( oldd != newd ) { int i, knot_count = ON_KnotCount(order,cv_count); for ( i = 0; i < knot_count; i++ ) { knot[i] = newd.ParameterAt(oldd.NormalizedParameterAt(knot[i])); } } rc = true; } return rc; }
bool ON_IsKnotVectorUniform( int order, int cv_count, const double* knot ) { bool rc = (order >= 2 && cv_count >= order && 0 != knot); if (rc) { const double delta = knot[order-1] - knot[order-2]; const double delta_tol = ON_SQRT_EPSILON*delta; int i0, i1; double d; if ( ON_IsKnotVectorClamped(order,cv_count,knot) ) { i0 = order; i1 = cv_count; } else { i0 = 1; i1 = ON_KnotCount(order,cv_count); } for (/*empty*/; i0 < i1 && rc; i0++ ) { d = knot[i0] - knot[i0-1]; if ( fabs(d - delta) > delta_tol ) rc = false; } } return rc; }
double ON_KnotTolerance( int order, int cv_count, const double* knot, int knot_index ) { const int knot_count = ON_KnotCount( order, cv_count ); int i0, i1, j; double a, b, tol; i0 = knot_index-order+1; if ( i0 < 0 ) i0 = 0; i1 = knot_index+order-1; if ( i1 >= knot_count ) i1 = knot_count-1; for ( j = knot_index; j > i0; j-- ) { if ( knot[j] != knot[knot_index] ) break; } a = fabs(knot[knot_index] - knot[j]); for ( j = knot_index; j < i1; j++ ) { if ( knot[j] != knot[knot_index] ) break; } b = fabs(knot[knot_index] - knot[j]); tol = (a==0.0 && b==0.0) ? 0.0 : (a + b + fabs(knot[knot_index]))* ON_SQRT_EPSILON; return tol; }
int ON_CompareKnotVector( // returns // -1: first < second // 0: first == second // +1: first > second int orderA, int cv_countA, const double* knotA, int orderB, int cv_countB, const double* knotB ) { const int knot_count = ON_KnotCount(orderA,cv_countA); int i; double a, b, atol, btol, ktol, tol; if ( orderA < orderB ) return -1; if ( orderA > orderB ) return 1; if ( cv_countA < cv_countB ) return -1; if ( cv_countA > cv_countB ) return 1; if ( !ON_GetKnotVectorDomain( orderA, cv_countA, knotA, &a, &b ) ) return -1; atol = ON_DomainTolerance( a, b ); if ( !ON_GetKnotVectorDomain( orderA, cv_countA, knotA, &a, &b ) ) return 1; btol = ON_DomainTolerance( a, b ); tol = (atol <= btol) ? atol : btol; for ( i = 0; i < knot_count; i++ ) { a = knotA[i]; b = knotB[i]; if ( a == b ) continue; if ( a < b-tol ) return -1; if ( b < a-tol ) return 1; atol = ON_KnotTolerance( orderA, cv_countA, knotA, i ); btol = ON_KnotTolerance( orderB, cv_countB, knotB, i ); ktol = (atol <= btol) ? atol : btol; if ( a < b-ktol ) return -1; if ( b < a-ktol ) return 1; } return 0; }
bool ON_KnotVectorHasBezierSpans( int order, // order (>=2) int cv_count, // cv count const double* knot // knot[] array ) { int knot_count = ON_KnotCount( order, cv_count ); if ( knot_count < 2 ) return false; int span_count = ON_KnotVectorSpanCount( order, cv_count, knot ); if ( span_count < 1 ) return false; if ( order >= 2 && cv_count >= order && knot_count == (span_count+1)*(order-1) && knot[0] == knot[order-2] && knot[cv_count-1] == knot[knot_count-1]) return true; return false; }
// Description: // Fill in knot values for a clamped uniform knot // vector. // Parameters: // order - [in] (>=2) order (degree+1) of the NURBS // cv_count - [in] (>=order) total number of control points // in the NURBS. // knot - [in/out] Input is an array with room for // ON_KnotCount(order,cv_count) doubles. Output is // a periodic uniform knot vector with domain // (0, (1+cv_count-order)*delta). // delta - [in] (>0, default=1.0) spacing between knots. // Returns: // true if successful ON_DECL bool ON_MakePeriodicUniformKnotVector( int order, int cv_count, double* knot, double delta ) { bool rc = false; if ( order >= 2 && cv_count >= order && knot != NULL && delta > 0.0 ) { double k = 0.0; int i, knot_count = ON_KnotCount(order,cv_count); for ( i = order-2, k = 0.0; i < knot_count; i++, k += delta ) knot[i] = k; for ( i = order-3, k = -delta; i >= 0; i--, k -= delta ) knot[i] = k; rc = true; } return rc; }
void RSpline::updateFromControlPoints() const { #ifndef R_NO_OPENNURBS if (controlPoints.size()<degree+1) { invalidate(); qWarning() << "RSpline::updateFromControlPoints: not enough control points: " << controlPoints.size(); return; } // periodic: if (periodic && !hasFitPoints()) { ON_3dPoint* points = new ON_3dPoint[controlPoints.size()]; for (int i=0; i<controlPoints.size(); ++i) { RVector cp = controlPoints.at(i); points[i] = ON_3dPoint(cp.x, cp.y, cp.z); } curve.CreatePeriodicUniformNurbs(3, getOrder(), controlPoints.size(), points); delete[] points; } // open or from fit points: else { curve.Create(3, false, getOrder(), controlPoints.size()); // setting control points: for (int i=0; i<controlPoints.size(); ++i) { RVector cp = controlPoints.at(i); ON_3dPoint onp(cp.x, cp.y, cp.z); curve.SetCV(i, onp); //qDebug() << "RSpline: controlPoints[" << i << "]: " << cp; } bool knotCondition = (knotVector.size() == getOrder() + controlPoints.size() - 2); //knotCondition = true; // genetate knot vector automatically: if (knotVector.isEmpty() || !knotCondition) { // if (!knotVector.isEmpty()) { // qDebug() << "RSpline: knotVector ignored"; // qDebug() << "RSpline: knots: " << knotVector.size(); // qDebug() << "RSpline: order: " << getOrder(); // qDebug() << "RSpline: controlPoints: " << controlPoints.size(); // } int si = ON_KnotCount(getOrder(), controlPoints.size()); double* knot = new double[si]; //ON_MakePeriodicUniformKnotVector(getOrder(), controlPoints.size(), knot); ON_MakeClampedUniformKnotVector(getOrder(), controlPoints.size(), knot); for (int i=0; i<si; ++i) { // qDebug() << "RSpline: knot[" << i << "]: " << knot[i]; curve.SetKnot(i, knot[i]); } delete[] knot; } else { int k=0; for (int i=0; i<knotVector.count(); ++i) { //qDebug() << "RSpline: knot[" << i << "]: " << knotVector.at(i); bool ok = curve.SetKnot(k++, knotVector.at(i)); if (!ok) { //qDebug() << "RSpline: knot[" << i << "]: NOT set"; } } } } //##getExploded(); #endif }
int ON_InsertKnot( double knot_value, int knot_multiplicity, int cv_dim, // dimension of cv's = ( = dim+1 for rational cvs ) int order, int cv_count, int cv_stride, double* cv, // NULL or cv array with room for at least knot_multiplicity new cvs double* knot, // knot array with room for at least knot_multiplicity new knots int* hint // optional hint about where to search for span to add knots to // pass NULL if no hint is available ) { int rc = 0; // return code = number of knots added if ( order < 2 || cv_count < order || !knot ) { ON_ERROR("ON_InsertKnot(): illegal input" ); return 0; } if ( cv ) { if ( cv_dim < 1 || cv_stride < cv_dim ) { ON_ERROR("ON_InsertKnot(): illegal input" ); return 0; } } if ( knot_multiplicity >= order ) { ON_ERROR("ON_InsertKnot(): requested knot_multiplicity > degree" ); return 0; } // shift knot vector and cv array so that knot_value lies in first span int span_index = ON_NurbsSpanIndex( order, cv_count, knot, knot_value, 1, hint?*hint:0 ); knot += span_index; if ( cv ) cv += (span_index*cv_stride); cv_count -= span_index; const double knot_tolerance = ON_SpanTolerance( order, cv_count, knot, 0 ); // check that knot_value is interior to NURBS domain if ( span_index == 0 ) { if ( knot_value < knot[order-1] ) { if ( knot_value <= knot[order-2] + knot_tolerance ) { ON_ERROR("ON_InsertKnot(): requested knot_value at start of NURBS domain" ); return 0; } } } if ( span_index == cv_count-order ) { if ( knot_value > knot[order-2] && knot_value >= knot[order-1] - knot_tolerance ) { ON_ERROR("ON_InsertKnot(): requested knot_value at end of NURBS domain" ); return 0; } } // if knot_value is nearly equal to an existing knot, make it exactly equal if ( knot_value <= 0.5*(knot[order-2]+knot[order-1]) && fabs( knot_value - knot[order-2] ) <= knot_tolerance ) { knot_value = knot[order-2]; } else if ( fabs( knot_value - knot[order-1] ) <= knot_tolerance ) { knot_value = knot[order-1]; } const int degree = order-1; // set m = number of knots to add int m = 0; int j; if ( knot_value == knot[order-2] ) { for ( j = order-2; m < knot_multiplicity && knot[j-m] == knot_value; m++ ) ; // empty for } else if ( knot_value == knot[order-1] ) { for ( j = order-1; m < knot_multiplicity && knot[j+m] == knot_value; m++ ) ; // empty for } m = knot_multiplicity - m; if ( hint ) *hint = span_index+m; if ( m <= 0 ) return 0; // no knots need to be added double* new_knot = (double*)onmalloc( ((2*degree+m) + (order+m)*cv_dim)*sizeof(*new_knot) ); if ( !new_knot ) { ON_ERROR("ON_InsertKnot(): out of memory"); return 0; } double* new_cv = 0; memcpy( new_knot, knot, 2*degree*sizeof(*new_knot) ); if ( cv ) { new_cv = new_knot + (2*degree+m); for ( j = 0; j < order; j++ ) { memcpy( new_cv + j*cv_dim, cv + j*cv_stride, cv_dim*sizeof(*new_cv) ); } } // add m more knots at knot_value rc = 0; while (m>0) { if ( !ON_InsertSingleKnot(cv_dim,order,cv_dim,new_cv,new_knot,knot_value) ) break; m--; if ( new_cv ) new_cv += cv_stride; new_knot++; rc++; } new_knot -= rc; new_cv -= rc*cv_stride; if ( rc > 0 ) { // make room for rc many new knots int i0 = ON_KnotCount( order, cv_count ) - 1; // knot[i0] = last input knot int i1 = i0 + rc; int j = (cv_count-order); while (j--) knot[i1--] = knot[i0--]; // update knot vector memcpy ( knot+degree, new_knot+degree, (degree+rc)*sizeof(*new_knot) ); if ( cv ) { // make room for rc many new CVs i0 = (cv_count-1)*cv_stride; // cv[i0] = last coord of last input cv */ i1 = i0 + rc*cv_stride; j = cv_count-order; while (j--) { memcpy( cv+i1, cv+i0, cv_dim*sizeof(*cv) ); i1 -= cv_stride; i0 -= cv_stride; } // update cv values for ( j = 0; j < order+rc; j++ ) { memcpy( cv, new_cv, cv_dim*sizeof(*new_cv) ); cv += cv_stride; new_cv += cv_dim; } } } onfree(new_knot); return rc; }
bool ON_GetGrevilleKnotVector( // get knots from Greville abcissa int g_stride, const double *g, // if not periodic, g[cv_count], // if periodic, g[cv_count-order+2] // usually, g[0] = 0, g[i] = |P[i]-P[i-1]|^q bool bPeriodic, int order, int cv_count, double* knot ) { bool rc = false; double* p = NULL; int ki, knot_count, g_count, gi, j, degree; double k, dd; if ( g_stride < 1 || !g || !knot || order < 2 || cv_count < order ) return false; if ( bPeriodic && order == 2 ) return false; if ( bPeriodic && cv_count - order + 2 < 3 ) return false; degree = order-1; if ( degree == 1 ) { for ( j = 0; j < cv_count; j++ ) { knot[j] = g[j*g_stride]; } return true; } dd = 1.0/degree; knot_count = ON_KnotCount( order, cv_count ); g_count = (bPeriodic) ? cv_count-order+2 : cv_count; if ( bPeriodic ) { int half_degree = (degree%2) ? degree/2 : 0; // step 1: set p[] = fully periodic list of abcissa p = (double*)onmalloc((g_count + 2*degree)*sizeof(*p)); for ( j = 0, gi = g_count-order; j < degree; j++, gi++ ) { p[j] = g[0] - g[g_count-1] + g[gi]; } for ( gi = 0, j = degree; gi < g_count; gi++, j++ ) { p[j] = g[gi]; } for ( j = g_count+degree, gi = 1; j < g_count+2*degree; j++, gi++ ) { p[j] = g[g_count-1] - g[0] + g[gi]; } // step 2: set new p[i] = old (p[i] + ... + p[i+degree-1]) / degree for ( j = 0; j < g_count+order; j++ ) { k = p[j]; for ( ki = 1; ki < degree; ki++ ) k += p[j+ki]; k *= dd; if ( half_degree ) { // if g[]'s are uniform and degree is odd, then knots = g[]'s if ( fabs(k-p[j+half_degree]) <= ON_SQRT_EPSILON*(p[j+degree-1]-p[j]) ) k = p[j+half_degree]; } p[j] = k; } // step 3: determine where g[0] maximizes NURBS basis functions { double* B = (double*)alloca(order*order*sizeof(B[0])); double maxB = 0.0; int maxBj = 0; for ( j = 0; j < 2*degree; j++ ) { if ( g[0] > p[j+degree] ) continue; if ( g[0] < p[j+degree-1] ) break; ON_EvaluateNurbsBasis( order, p+j, g[0], B ); if ( B[0] > maxB ) { maxB = B[0]; maxBj = j; } } memcpy( knot, &p[maxBj], knot_count*sizeof(*knot) ); } rc = ON_MakeKnotVectorPeriodic( order, cv_count, knot ); } else { // clamped knots rc = true; if ( g > knot && g < knot+knot_count ) { p = (double*)onmalloc(cv_count*sizeof(*p)); for( j = 0; j < cv_count; j++ ) { p[j] = g[j*g_stride]; } g = p; g_stride = 1; } for ( ki = 0; ki < degree; ki++ ) { knot[ki] = g[0]; } for ( ki = degree, gi = 1; ki < cv_count; ki++, gi++ ) { k = 0.0; for ( j = 0; j < degree; j++ ) { k += g[(gi+j)*g_stride]; } knot[ki] = k*dd; if ( knot[ki] < knot[ki-1] || knot[ki] <= knot[ki-degree] ) { rc = false; } } for ( ki = cv_count-1; ki < knot_count; ki++ ) { knot[ki] = g[(cv_count-1)*g_stride]; } } if (p) onfree(p); return rc; }