/* $NoKeywords: $ */ /* // // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved. // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert // McNeel & Associates. // // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF // MERCHANTABILITY ARE HEREBY DISCLAIMED. // // For complete openNURBS copyright information see . // //////////////////////////////////////////////////////////////// */ #include "opennurbs.h" #if !defined(ON_COMPILING_OPENNURBS) // This check is included in all opennurbs source .c and .cpp files to insure // ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. // When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined // and the opennurbs .h files alter what is declared and how it is declared. #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif double ON_EvaluateBernsteinBasis(int degree, int i, double t) /***************************************************************************** Evaluate Bernstein basis polynomial INPUT: degree If degree < 0, then 0.0 is returned. i If i < 0 or i > degree, then 0.0 is returned. t The formula for the Bernstein polynomial is valid for all values of t. OUTPUT: TL_EvBernsteinBasis() degree! ---------------- * (1-t)^(degree-i) * t^i, if 0 <= i <= degree (degree-i)! * i! 0, otherwise. (In this function, 0^0 is treated as 1.) COMMENTS: Below, B(d,i,t) is used to denote the i-th Bernstein basis polynomial of degree d; i.e., B(d,i,t) = TL_EvBernsteinBasis(d,i,t). When degree <= 4, TL_EvBernsteinBasis() computes the value directly. When 4 < degree < 9, the value is computed recursively using the formula B(d,i,t) = t*B(d-1,i-1,t) + (1-t)*B(d-1,i,t). For 9 <= degree, the value is computed using the formula B(d,i,t) = TL_EvBinomial(degree-i,i) *((degree==i) ? 1.0 : pow(1.0-t,(double)(degree-i))) *((i) ? pow(t,(double)i) : 1.0); The value of a degree d Bezier at t with control vertices {P_0, ..., P_d} is equal to B(d,0,t)*P_0 + ... + B(d,d,t)*P_d. Numerically, this formula is inefficient and unstable. The de Casteljau algorithm used in TL_EvdeCasteljau() is faster and more stable. EXAMPLE: // Use TL_EvBernsteinBasis() to evaluate a 3 dimensional // non-rational cubic Bezier at 1/4. double cv[4][3], t, B[4], answer[3]; int i, j, degree; degree = 3; t = 0.25; answer[0] = answer[1] = answer[2] = 0.0; for (i = 0; i <= degree; i++) cv[i] = something; for (i = 0; i <=degree; i++) { B[i] = TL_EvBernsteinBasis(degree,i,t); for (j = 0; j < 3; j++) answer[j] += B[i]*cv[i][j]; } REFERENCE: BOHM-01, Page 7. RELATED FUNCTIONS: TL_EvNurbBasis TL_EvdeCasteljau() TL_EvBezier() TL_EvHorner() *****************************************************************************/ { double s; if (degree < 0 || i < 0 || i > degree) return 0.0; switch(degree) { case 0: /* degree 0 */ return 1.0; case 1: /* degree 1 */ return ((i) ? t : 1.0-t); case 2: /* degree 2 */ switch(i) { case 0: t = 1.0-t; return t*t; case 1: return 2.0*t*(1.0-t); default: /* i == 2 */ return t*t; } case 3: /* degree 3 */ switch(i) { case 0: t = 1.0 - t; return t*t*t; case 1: s = 1.0-t; return 3.0*s*s*t; case 2: return 3.0*(1.0-t)*t*t; default: /* i == 3 */ return t*t*t; } case 4: /* degree 4 */ switch(i) { case 0: t = 1.0-t; t = t*t; return t*t; case 1: /* 4*(1-t)^3*t */ s = 1.0-t; return 4.0*s*s*s*t; case 2: /* 6*(1-t)^2*t */ s = 1.0-t; return 6.0*s*s*t*t; case 3: /* 4*(1-t)*t^3 */ return 4.0*(1.0-t)*t*t*t; default: /* t^4 (i == 4) */ t = t*t; return t*t; } default: /* degree >= 5 */ /* The "9" was determined to produce the fastest code when * tested on a SUN Sparc (SUNOS 4.3, gcc -O) */ if (degree < 9) return (t*ON_EvaluateBernsteinBasis(degree-1,i-1,t) + (1-t)*ON_EvaluateBernsteinBasis(degree-1,i,t)); else return ON_BinomialCoefficient(degree-i,i)*((degree==i)?1.0:pow(1.0-t,(double)(degree-i)))*((i)?pow(t,(double)i):1.0); } } void ON_EvaluatedeCasteljau(int dim, int order, int side, int cv_stride, double* cv, double t) /***************************************************************************** Evaluate a Bezier using the de Casteljau algorithm INPUT: dim ( >= 1) order ( >= 2) side <= 0 return left side of bezier in cv array > 0 return right side of bezier in cv array cv array of order*cv_stride doubles that specify the Bezier's control vertices. cv_stride ( >= dim) number of doubles between cv's (typically a multiple of dim). t If side <= 0, then t must be > 0.0. If side > 0, then t must be < 1.0. OUTPUT: cv If side <= 0, the input cv's are replaced with the cv's for the bezier trimmed/extended to [0,t]. In particular, {cv[(order-1)*cv_stride], ..., cv[order*cv_stride - 1]} is the value of the Bezier at t. If side > 0, the input cv's are replaced with the cv's for the Bezier trimmed/extended to [t,1]. In particular, {cv[0], ..., cv[dim-1]} is the value of the Bezier at t. COMMENTS: Set C[i,j] = {cv[j*cv_stride], ..., cv[(j+1)*cv_stride-1]}, if i = 0 (1-t)*C[i-1,j-1] + t*C[i-1,j], if 0 < i <= d = degree The collection of C[i,j]'s is typically drawn in a triangular array: C[0,0] C[1,1] C[0,1] C[2,2] C[1,2] ... C[0,2] ... C[d,d] ... C[0,d-1] C[2,d] C[1,d] C[0,d] The value of the Bezier at t is equal to C[d,d]. When side < 0, the input cv's are replaced with C[0,0], C[1,2], ..., C[d,d]. If the output cv's are used as control vertices for a Bezier, then output_bezier(s) = input_bezier(t*s). When side >= 0, the input cv's are replace with C[d,d], C[d-1,d], ..., C[0,d]. If the output cv's are used as control vertices for a Bezier, then output_bezier(s) = input_bezier((1-s)*t + s). If a Bezier is going to be evaluated more than a few times, it is faster to convert the Bezier to power basis and evaluate using TL_EvHorner. However, Horner's algorithm is not a stable as de Casteljau's. EXAMPLE: // Use TL_EvdeCasteljau() to trim/extend a Bezier // to the interval [t0,t1], where t0 < t1 double cv[order][dim], t0, t1 cv = whatever; if (1.0 - t0 > t1) { // first trim at t0, then trim at t1 if (t0 != 0.0) TL_EvdeCasteljau(dim,order, 1,cv,dim,t0); t1 = (t1-t0)/(1.0 - t0); // adjust t1 to new domain if (t1 != 1.0) TL_EvdeCasteljau(dim,order,-1,cv,dim,t1); } else { // first trim at t1, then trim at t0 if (t1 != 1.0) TL_EvdeCasteljau(dim,order,-1,cv,dim,t1); t0 /= t1; // adjust t0 to new domain if (t0 != 0.0) TL_EvdeCasteljau(dim,order, 1,cv,dim,t0); } REFERENCE: BOHM-01, Page 8. RELATED FUNCTIONS: TL_EvBernsteinBasis TL_EvBezier TL_EvdeBoor TL_EvHorner TL_ConvertBezierToPolynomial *****************************************************************************/ { double s, *P0, *P1; int j, d, off_minus_dim; if (t == 0.0 || t == 1.0) return; s = 1.0 - t; /* it's ugly and it's fast */ if (cv_stride > dim) { off_minus_dim = cv_stride - dim; if (side > 0) { /* output cv's = bezier trimmed to [t,1] */ while (--order) { P0 = cv; /* first cv */ P1 = P0 + cv_stride; /* next cv */ j = order; while (j--) { d = dim; while (d--) {*P0 = (*P0 * s) + (*P1 * t); P0++; P1++;} P0 += off_minus_dim; P1 += off_minus_dim;}} } else { /* side <= 0, so output cv's = bezier trimmed to [0,t] */ cv += order*dim; /* now cv = last control vertex */ while (--order) { P1 = cv; /* last cv */ P0 = P1 - cv_stride; /* next to last cv */ j = order; while (j--) { d = dim; while (d--) {P0--; P1--; *P1 = (*P0 * s) + (*P1 * t);} P0 -= off_minus_dim; P1 -= off_minus_dim;}} } } else { if (side > 0) { /* output cv's = bezier trimmed to [t,1] */ while (--order) { P0 = cv; /* first cv */ P1 = P0 + dim; /* next cv */ j = order; while (j--) { d = dim; while (d--) {*P0 = (*P0 * s) + (*P1 * t); P0++; P1++;}} } } else { /* side <= 0, so output cv's = bezier trimmed to [0,t] */ cv += order*dim; /* now cv = last control vertex */ while (--order) { P1 = cv; /* last cv */ P0 = P1 - dim; /* next to last cv */ j = order; while (j--) { d = dim; while (d--) {P0--; P1--; *P1 = (*P0 * s) + (*P1 * t);}} } } } } bool ON_IncreaseBezierDegree( int dim, bool is_rat, int order, int cv_stride, double* cv ) /***************************************************************************** Increase the degree of a Bezier INPUT: cvdim (dim + is_rat) order ( >= 2 ) order of input bezier cv control vertices of bezier newcv array of cvdim*(order+1) doubles (The cv and newcv pointers may be equal.) OUTPUT: newcv Control vertices of an Bezier with order (order+1). The new Bezier and the old Bezier evaluate to the same point. COMMENTS: If {B_0, ... B_d} are the control vertices of the input Bezier, then {C_0, ..., C_{d+1}} are the control vertices of the returned Bezier, where, C_0 = B_0 C_k = k/(d+1) * B_{k-1} + (d+1-k)/(d+1) * B_{k}(1 < k <= d) C_{d+1} = B_d The computation is done in a way that permits the pointers cv and newcv to be equal; i.e., if the cv array is long enough, the degree may be raised with a call like TL_IncreaseBezierDegree(cvdim,order,cv,cv); EXAMPLE: raise_degree(TL_BEZIER* bez) { // raise the degree of a TL_BEZIER bez->cv = (double*) onrealloc ( bez->cv, (bez->order+1)*(bez->dim+bez->is_rat) ); TL_IncreaseBezierDegree ( bez->dim+bez->is_rat, bez->order,bez->cv,bez->cv ); bez->order++; } REFERENCE: BOHM-01, Page 7. RELATED FUNCTIONS: TL_DecreaseBezierDegree *****************************************************************************/ { double a0, a1, d, c0, c1; int j; double* newcv = cv; const int cvdim = (is_rat)?dim+1:dim; const int dcv = cv_stride - cvdim; j = cv_stride*order; newcv += j; memcpy( newcv, newcv-cv_stride, cvdim*sizeof(*newcv) ); newcv -= (dcv+1); cv = newcv - cv_stride; a0 = order; a1 = 0.0; d = 1.0/a0; while (--order) { a0 -= 1.0; a1 += 1.0; c0 = d*a0; c1 = d*a1; j = cvdim; while(j--) { *newcv = c0 * *cv + c1 * *newcv; cv--; newcv--; } cv -= dcv; newcv -= dcv; } return true; } bool ON_RemoveBezierSingAt0( int dim, int order, int cv_stride, double* cv ) { const int cvdim = dim+1; int j,k,ord0; ord0 = order; while(cv[dim] == 0.0) { order--; if (order < 2) return false; j = dim; while(j--) { if (cv[j] != 0.0) return false; } for (j=0; j 1 && cv[CVlen-1] == 0.0) { order--; if (order < 2) return false; i = dim; while(i--) { if (cv[CVlen-1-i] != 0.0) return false; } for (i=0; i= (is_rat)?dim+1:dim const double* cv, // cv[order*cv_stride] array double t0, double t1, // domain int der_count, // number of derivatives to compute double t, // evaluation parameter int v_stride, // v_stride (>=dimension) double* v // v[(der_count+1)*v_stride] array ) /***************************************************************************** Evaluate a Bezier INPUT: dim (>= 1) dimension of Bezier's range is_rat 0: bezier is not rational 1: bezier is rational order (>= 2) (order = degree+1) cv array of (dim+is_rat)*order doubles that define the Bezier's control vertices. t0, t1 (t0 != t1) Bezier's domain. Mathematically, Beziers have domain [0,1]. In practice Beziers are frequently evaluated at (t-t0)/(t1-t0) and the chain rule is used to evaluate the derivatives. This function is the most efficient place to apply the chain rule. t Evaluation parameter der_count (>= 0) number of derivatives to evaluate answer answer[i] is nullptr or points to an array of dim doubles. OUTPUT: ON_EvBezier() 0: successful -1: failure - rational function had nonzero numerator and zero denominator answer bez(t) = answer[0] bez'(t) = answer[1] ... (n) bez (t) = answer[n] COMMENTS: Use de Casteljau's algorithm. Rational fuctions with removable singularities (like x^2/x) are efficiently and correctly handled. EXAMPLE: // ... REFERENCE: AUTHOR page ? RELATED FUNCTIONS: ON_EvaluatedeCasteljau ON_EvQuotientRule ON_EvNurb ON_EvPolynomialPoint ON_onvertBezierToPolynomial ON_onvertPolynomialToBezier ON_onvertNurbToBezier *****************************************************************************/ { unsigned char stack_buffer[4*64*sizeof(double)]; double delta_t; double alpha0; double alpha1; double *cv0, *cv1; int i, j, k; double* CV, *tmp; void* free_me = 0; const int degree = order-1; const int cvdim = (is_rat)?dim+1:dim; if ( cv_stride < cvdim ) cv_stride = cvdim; memset( v, 0, v_stride*(der_count+1)*sizeof(*v) ); if (!(t0!=t1)) { // Fix http://mcneel.myjetbrains.com/youtrack/issue/RH-28304 // This test for valid domain was being done only in debug builds // and not in release builds. The #if defined(ON_DEBUG) projection // has been here since 2005 when we switched to svn. // I can't determine when or why it got added because it happened // when we used SourceSafe or earlier version control. // The bug was a crash in release builds, which skipped this test. // I'm enabling the test in all builds and addeing a call to ON_ERROR(). ON_ERROR("Invalid domain"); return false; } i = order*cvdim; j = 0; if (der_count > degree) { if (is_rat) j = (der_count-degree)*cvdim; else { der_count = degree; } } size_t sizeofCV = (i+j)*sizeof(*CV); CV = (double*)( (sizeofCV <= sizeof(stack_buffer)) ? stack_buffer : (free_me=onmalloc(sizeofCV)) ); if (j) { memset( CV+i, 0, j*sizeof(*CV) ); } cv0=CV; if ( t0 == t || (t <= 0.5*(t0+t1) && t != t1) ) { for ( i = 0; i < order; i++ ) { memcpy( cv0, cv, cvdim*sizeof(*cv0) ); cv0 += cvdim; cv += cv_stride; } cv -= (cv_stride*order); delta_t = 1.0/(t1 - t); alpha1 = 1.0/(t1-t0); alpha0 = (t1-t)*alpha1; alpha1 *= t-t0; } else { cv += (cv_stride*order); k=order; while(k--) { cv -= cv_stride; memcpy( cv0, cv, cvdim*sizeof(*cv0) ); cv0 += cvdim; } delta_t = 1.0/(t0 - t); alpha0 = 1.0/(t1-t0); alpha1 = (t1-t)*alpha0; alpha0 *= t-t0; } /* deCasteljau (from the right) */ if (alpha1 != 0.0) { j = order; while (--j) { cv0 = CV; cv1 = cv0 + cvdim; i = j; while (i--) { k = cvdim; while (k--) { *cv0 = *cv0 * alpha0 + *cv1 * alpha1; cv0++; cv1++; } } } } /* check for removable singularity */ if (is_rat && CV[dim] == 0.0) { if ( !ON_RemoveBezierSingAt0(dim,order,cvdim,CV) ) { if ( free_me ) onfree(free_me); return false; } } /* Lee (from the right) */ if (der_count) { tmp=CV; alpha0 = order; j = (der_count>=order)?order:der_count+1; CV += cvdim*j; while(--j) { alpha0 -= 1.0; cv1 = CV; cv0 = cv1-cvdim; i=j; while(i--) { alpha1 = alpha0 * delta_t; k=cvdim; while(k--) { cv0--; cv1--; *cv1 = alpha1*(*cv1 - *cv0); } } } CV=tmp; } if ( 2 == order ) { // 7 January 2004 Dale Lear // Added to fix those cases when, numerically, t*a + (1.0-t)*a != a. // Similar to fix for RR 9683. j = cv_stride; for ( i = 0; i < cvdim; i++, j++ ) { if ( cv[i] == cv[j] ) { CV[i] = cv[i]; } } } if (is_rat) { ON_EvaluateQuotientRule( dim, der_count, cvdim, CV ); } for (i=0;i<=der_count;i++) { memcpy( v, CV, dim*sizeof(*v) ); v += v_stride; CV += cvdim; } if ( free_me ) onfree(free_me); return true; } bool ON_EvaluateNurbsBasis( int order, const double* knot, double t, double* N ) { double a0, a1, x, y; const double *k0; double *t_k, *k_t, *N0; const int d = order-1; int j, r; double stack_buffer[80]; void* heap_buffer = 0; const size_t sizeof_buffer = d<<4; t_k = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)(heap_buffer=onmalloc( sizeof_buffer )); k_t = t_k + d; if (knot[d-1] == knot[d]) { /* value is defined to be zero on empty spans */ memset( N, 0, order*order*sizeof(*N) ); return true; } N += order*order-1; N[0] = 1.0; knot += d; k0 = knot - 1; for (j = 0; j < d; j++ ) { N0 = N; N -= order+1; t_k[j] = t - *k0--; k_t[j] = *knot++ - t; x = 0.0; for (r = 0; r <= j; r++) { a0 = t_k[j-r]; a1 = k_t[r]; y = N0[r]/(a0 + a1); N[r] = x + a1*y; x = a0*y; } N[r] = x; } // 16 September 2003 Dale Lear (at Chuck's request) // When t is at an end knot, do a check to // get exact values of basis functions. // The problem being that a0*y above can // fail to be one by a bit or two when knot // values are large. x = 1.0-ON_SQRT_EPSILON; if ( N[0] >= x ) { if ( N[0] != 1.0 && N[0] <= 1.0 + ON_SQRT_EPSILON ) { r = 1; for ( j = 1; j <= d; j++ ) { if (N[j] != 0.0) { r = 0; break; } } if (r) N[0] = 1.0; } } else if ( N[d] >= x ) { if ( N[d] != 1.0 && N[d] <= 1.0 + ON_SQRT_EPSILON ) { r = 1; for ( j = 0; j < d; j++ ) { if ( N[j] != 0.0 ) { r = 0; break; } } if (r) N[d] = 1.0; } } if ( heap_buffer ) onfree(heap_buffer); return true; } bool ON_EvaluateNurbsBasisDerivatives( int order, const double* knot, int der_count, double* N ) { double dN, c; const double *k0, *k1; double *a0, *a1, *ptr, **dk; int i, j, k, jmax; const int d = order-1; const int Nstride = -der_count*order; /* workspaces for knot differences and coefficients * * a0[] and a1[] have order doubles * * dk[0] = array of d knot differences * dk[1] = array of (d-1) knot differences * * dk[der_count-1] = 1.0/(knot[d] - knot[d-1]) * dk[der_count] = dummy pointer to make loop efficient */ //dk = (double**)alloca( (der_count+1) << 3 ); /* << 3 in case pointers are 8 bytes long */ //a0 = (double*)alloca( (order*(2 + ((d+1)>>1))) << 3 ); /* d for a0, d for a1, d*order/2 for dk[]'s and slop to avoid /2 */ //a1 = a0 + order; double stack_buffer[80]; void* heap_buffer = 0; const size_t dbl_count = (order*(2 + ((d+1)>>1))); const size_t sz = ( dbl_count*sizeof(*a0) + (der_count+1)*sizeof(*dk) ); a0 = (sz <= sizeof(stack_buffer)) ? stack_buffer : (double*)(heap_buffer = onmalloc(sz)); a1 = a0 + order; dk = (double**)(a0 + dbl_count); /* initialize reciprocal of knot differences */ dk[0] = a1 + order; for (k = 0; k < der_count; k++) { j = d-k; k0 = knot++; k1 = k0 + j; for (i = 0; i < j; i++) dk[k][i] = 1.0/(*k1++ - *k0++); dk[k+1] = dk[k] + j; } dk--; /* dk[1] = 1/{t[d]-t[0], t[d+1]-t[1], ..., t[2d-2] - t[d-2], t[2d-1] - t[d-1]} * = diffs needed for 1rst derivative * dk[2] = 1/{t[d]-t[1], t[d+1]-t[2], ..., t[2d-2] - t[d-1]} * = diffs needed for 2nd derivative * ... * dk[d] = 1/{t[d] - t[d-1]} * = diff needed for d-th derivative * * d[k][n] = 1.0/( t[d+n] - t[k-1+n] ) */ N += order; /* set N[0] ,..., N[d] = 1rst derivatives, * N[order], ..., N[order+d] = 2nd, etc. */ for ( i=0; i= (is_rat)?dim+1:dim const double* cv, // cv[order*cv_stride] array int der_count, // number of derivatives to compute double t, // evaluation parameter int v_stride, // v_stride (>=dimension) double* v // v[(der_count+1)*v_stride] array ) { const int stride_minus_dim = cv_stride - dim; const int cv_len = cv_stride*order; int i, j, k; double *N; double a; double stack_buffer[64]; void* heap_buffer = 0; const size_t sizeof_buffer = (order*order)*sizeof(*N); N = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)(heap_buffer=onmalloc(sizeof_buffer)); if ( stride_minus_dim > 0) { i = (der_count+1); while( i--) { memset(v,0,dim*sizeof(v[0])); v += v_stride; } v -= ((der_count+1)*v_stride); } else { memset( v, 0, (der_count+1)*v_stride*sizeof(*v) ); } if ( der_count >= order ) der_count = order-1; // evaluate basis functions ON_EvaluateNurbsBasis( order, knot, t, N ); if ( der_count ) ON_EvaluateNurbsBasisDerivatives( order, knot, der_count, N ); // convert cv's into answers for (i = 0; i <= der_count; i++, v += v_stride, N += order) { for ( j = 0; j < order; j++ ) { a = N[j]; for ( k = 0; k < dim; k++ ) { *v++ += a* *cv++; } v -= dim; cv += stride_minus_dim; } cv -= cv_len; } if ( 2 == order ) { // 7 January 2004 Dale Lear // Added to fix those cases when, numerically, t*a + (1.0-t)*a != a. // Similar to fix for RR 9683. v -= (der_count+1)*v_stride; j = cv_stride; for ( i = 0; i < dim; i++, j++ ) { if (cv[i] == cv[j] ) v[i] = cv[i]; } } if ( 0 != heap_buffer ) onfree(heap_buffer); return true; } static bool ON_EvaluateNurbsRationalSpan( int dim, // dimension int order, // order const double* knot, // knot[] array of (2*order-2) doubles int cv_stride, // cv_stride >= (is_rat)?dim+1:dim const double* cv, // cv[order*cv_stride] array int der_count, // number of derivatives to compute double t, // evaluation parameter int v_stride, // v_stride (>=dimension) double* v // v[(der_count+1)*v_stride] array ) { const int hv_stride = dim+1; double *hv; int i; bool rc; double stack_buffer[32]; void* heap_buffer = 0; const size_t sizeof_buffer = (der_count+1)*hv_stride*sizeof(*hv); hv = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)(heap_buffer=onmalloc(sizeof_buffer)); rc = ON_EvaluateNurbsNonRationalSpan( dim+1, order, knot, cv_stride, cv, der_count, t, hv_stride, hv ); if (rc) { rc = ON_EvaluateQuotientRule(dim, der_count, hv_stride, hv); if ( rc ) { // copy answer to v[] for ( i = 0; i <= der_count; i++ ) { memcpy( v, hv, dim*sizeof(*v) ); v += v_stride; hv += hv_stride; } } } if ( heap_buffer ) onfree(heap_buffer); return rc; } bool ON_EvaluateNurbsSpan( int dim, // dimension bool is_rat, // true if NURBS is rational int order, // order const double* knot, // knot[] array of (2*order-2) doubles int cv_stride, // cv_stride >= (is_rat)?dim+1:dim const double* cv, // cv[order*cv_stride] array int der_count, // number of derivatives to compute double t, // evaluation parameter int v_stride, // v_stride (>=dimension) double* v // v[(der_count+1)*v_stride] array ) { bool rc = false; if ( knot[0] == knot[order-2] && knot[order-1] == knot[2*order-3] ) { // Bezier span - use faster Bezier evaluator rc = ON_EvaluateBezier(dim, is_rat, order, cv_stride, cv, knot[order-2], knot[order-1], der_count, t, v_stride, v); } else { // generic NURBS span evaluation rc = (is_rat) ? ON_EvaluateNurbsRationalSpan( dim, order, knot, cv_stride, cv, der_count, t, v_stride, v ) : ON_EvaluateNurbsNonRationalSpan( dim, order, knot, cv_stride, cv, der_count, t, v_stride, v ); } return rc; } bool ON_EvaluateNurbsSurfaceSpan( int dim, bool is_rat, int order0, int order1, const double* knot0, const double* knot1, int cv_stride0, int cv_stride1, const double* cv0, // cv at "lower left" of bispan int der_count, double t0, double t1, int v_stride, double* v // returns values ) { const int der_count0 = (der_count >= order0) ? order0-1 : der_count; const int der_count1 = (der_count >= order1) ? order1-1 : der_count; const double *cv; double *N_0, *N_1, *P0, *P; double c; int d1max, d, d0, d1, i, j, j0, j1, Pcount, Psize; const int cvdim = (is_rat) ? dim+1 : dim; const int dcv1 = cv_stride1 - cvdim; double stack_buffer[128]; void* heap_buffer = 0; size_t sizeof_buffer; // get work space memory i = order0*order0; j = order1*order1; Pcount = ((der_count+1)*(der_count+2))>>1; Psize = cvdim<<3; sizeof_buffer = ((i + j) << 3) + Pcount*Psize; N_0 = (sizeof_buffer <= sizeof(stack_buffer)) ? stack_buffer : (double*)(heap_buffer=onmalloc(sizeof_buffer)); N_1 = N_0 + i; P0 = N_1 + j; memset( P0, 0, Pcount*Psize ); /* evaluate basis functions */ ON_EvaluateNurbsBasis( order0, knot0, t0, N_0 ); ON_EvaluateNurbsBasis( order1, knot1, t1, N_1 ); if (der_count0) { // der_count0 > 0 iff der_count1 > 0 ON_EvaluateNurbsBasisDerivatives( order0, knot0, der_count0, N_0 ); ON_EvaluateNurbsBasisDerivatives( order1, knot1, der_count1, N_1 ); } // compute point P = P0; for ( j0 = 0; j0 < order0; j0++) { cv = cv0 + j0*cv_stride0; for ( j1 = 0; j1 < order1; j1++ ) { c = N_0[j0]*N_1[j1]; j = cvdim; while (j--) *P++ += c* *cv++; P -= cvdim; cv += dcv1; } } if ( der_count > 0 ) { // compute first derivatives P += cvdim; // step over point for ( j0 = 0; j0 < order0; j0++) { cv = cv0 + j0*cv_stride0; for ( j1 = 0; j1 < order1; j1++ ) { // "Ds" c = N_0[j0+order0]*N_1[j1]; j = cvdim; while (j--) *P++ += c* *cv++; cv -= cvdim; // "Dt" c = N_0[j0]*N_1[j1+order1]; j = cvdim; while (j--) *P++ += c* *cv++; P -= cvdim; P -= cvdim; cv += dcv1; } } if ( der_count > 1 ) { // compute second derivatives P += cvdim; // step over "Ds" P += cvdim; // step over "Dt" if ( der_count0+der_count1 > 1 ) { // compute "Dss" for ( j0 = 0; j0 < order0; j0++) { // P points to first coordinate of Dss cv = cv0 + j0*cv_stride0; for ( j1 = 0; j1 < order1; j1++ ) { if ( der_count0 > 1 ) { // "Dss" c = N_0[j0+2*order0]*N_1[j1]; j = cvdim; while (j--) *P++ += c* *cv++; cv -= cvdim; } else { P += cvdim; // Dss = 0 } // "Dst" c = N_0[j0+order0]*N_1[j1+order1]; j = cvdim; while (j--) *P++ += c* *cv++; cv -= cvdim; if ( der_count1 > 1 ) { // "Dtt" c = N_0[j0]*N_1[j1+2*order1]; j = cvdim; while (j--) *P++ += c* *cv++; cv -= cvdim; P -= cvdim; } P -= cvdim; P -= cvdim; cv += cv_stride1; } } } if ( der_count > 2 ) { // 12 February 2004 Dale Lear // Bug fix for d^n/ds^n when n >= 3 // compute higher derivatives in slower generic loop for ( d = 3; d <= der_count; d++ ) { P += d*cvdim; // step over (d-1)th derivatives d1max = (d > der_count1) ? der_count1 : d; for ( j0 = 0; j0 < order0; j0++) { cv = cv0 + j0*cv_stride0; for ( j1 = 0; j1 < order1; j1++ ) { for (d0 = d, d1 = 0; d0 > der_count0 && d1 <= d1max; d0--, d1++ ) { // partial with respect to "s" is zero P += cvdim; } for ( /*empty*/; d1 <= d1max; d0--, d1++ ) { c = N_0[j0 + d0*order0]*N_1[j1 + d1*order1]; j = cvdim; while (j--) *P++ += c* *cv++; cv -= cvdim; } // remaining partials with respect to "t" are zero // - reset and add contribution from the next cv P -= d1*cvdim; cv += cv_stride1; } } } } } } if ( is_rat ) { ON_EvaluateQuotientRule2( dim, der_count, cvdim, P0 ); Psize -= 8; } for ( i = 0; i < Pcount; i++) { memcpy( v, P0, Psize ); v += v_stride; P0 += cvdim; } if ( heap_buffer ) onfree(heap_buffer); return true; } bool ON_EvaluateNurbsDeBoor( int cv_dim, int order, int cv_stride, double *cv, const double *knots, int side, double mult_k, double t ) /* * Evaluate a B-spline span using the de Boor algorithm * * INPUT: * cv_dim * >= 1 * order * (>= 2) There is no restriction on order. For order >= 18, * the necessary workspace is dynamically allocated and deallocated. * (The function requires a workspace of 2*order-2 doubles.) * cv * array of order*cv_dim doubles that specify the B-spline span's * control vertices. * knots * array of 2*(order-1) doubles that specify the B-spline span's * knot vector. * side * -1 return left side of B-spline span in cv array * +1 return right side of B-spline span in cv array * -2 return left side of B-spline span in cv array * Ignore values of knots[0,...,order-3] and assume * left end of span has a fully multiple knot with * value "mult_k". * +2 return right side of B-spline span in cv array * Ignore values of knots[order,...,2*order-2] and * assume right end of span has a fully multiple * knot with value "mult_k". * WARNING: If side is != {-2,-1,+1,+2}, this function may crash * or return garbage. * mult_k * Used when side = -2 or +2. * t * If side < 0, then the cv's for the portion of the NURB span to * the LEFT of t are computed. If side > 0, then the cv's for the * portion the span to the RIGHT of t are computed. The following * table summarizes the restrictions on t: * * value of side condition t must satisfy * -2 mult_k < t and mult_k < knots[order-1] * -1 knots[order-2] < t * +1 t < knots[order-1] * +2 t < mult_k and knots[order-2] < mult_k * * OUTPUT: * cv * If side <= 0, the input cv's are replaced with the cv's for * the B-spline span trimmed/extended to [knots[order-2],t] with * new knot vector {knots[0], ..., knots[order-2], t, ..., t}. * \________/ * order-1 t's * In particular, {cv[(order-1)*cv_dim], ..., cv[order*cv_dim - 1]} * is the value of the B-spline at t. * If side > 0, the input cv's are replaced with the cv's for * the B-spline span trimmed/extended to [t,knots[order-1]] with * new knot vector {t, ..., t, knots[order-1], ..., knots[2*order-3]}. * \________/ * order-1 t's * In particular, {cv[0], ..., cv[cv_dim-1]} is the value of the B-spline * at t. * * NOTE WELL: The input knot vector is NOT modified. If you want to * use the returned control points with the input knot vector, * then it is your responsibility to set * knots[0] = ... = knots[order-2] = t (side > 0) * or * knots[order-1] = ... = knots[2*order-2] = t (side < 0). * See the comments concering +/- 2 values of the "side" * argument. In most cases, you can avoid resetting knots * by carefully choosing the value of "side" and "mult_k". * TL_EvDeBoor() * true: successful * false: knot[order-2] == knot[order-1] * * COMMENTS: * * This function is the single most important NURB curve function in the * TL library. It is used to evaluate, trim, split and extend NURB curves. * It is used to convert portions of NURB curves to Beziers and to create * fully multiple end knots. The functions that perform the above tasks * simply call this function with appropriate values and take linear * combonations of the returned cv's to compute the desired result. * * Rational cases are handled adding one to the dimension and applying the * quotient rule as needed. * * Set a[i,j] = (t-knots[i+j-1])/(knots[i+j+order-2] - knots[i+j-1]) * Set D[i,j] = {cv[j*cv_dim], ..., cv[(j+1)*cv_dim-1]}, if i = 0 * (1-a[i,j])*D[i-1,j-1] + a[i,j]*D[i-1,j], if 0 < i <= d = degree * * The collection of D[i,j]'s is typically drawn in a triangular array: * * D[0,0] * D[1,1] * D[0,1] D[2,2] * D[1,2] ... * D[0,2] * * ... D[d,d] * ... * D[0,d-1] D[2,d] * D[1,d] * D[0,d] * * When side <= 0, the input cv's are replaced with * D[0,0], D[1,2], ..., D[d,d]. * * When side > 0, the input cv's are replace with * D[d,d], D[d-1,d], ..., D[0,d]. * * EXAMPLE: * * REFERENCE: * BOHM-01, Page 16. * LEE-01, Section 6. * * RELATED FUNCTIONS: * TL_EvNurbBasis(), TL_EvNurb(), TL_EvdeCasteljau(), TL_EvQuotientRule() */ { double workarray[21], alpha0, alpha1, t0, t1, dt, *delta_t, *free_delta_t, *cv0, *cv1; const double *k0, *k1; int degree, i, j, k; const int cv_inc = cv_stride - cv_dim; j = 0; delta_t = workarray; free_delta_t = 0; degree = order-1; t0 = knots[degree-1]; t1 = knots[degree]; if (t0 == t1) { ON_ERROR("ON_EvaluateNurbsDeBoor(): knots[degree-1] == knots[degree]"); return false; } if (side < 0) { /* if right end of span is fully multiple and t = end knot, * then we're done. */ if (t == t1 && t1 == knots[2*degree-1]) return true; /* if left end of span is fully multiple, save time */ if (side == -2) t0 = mult_k; else if (t0 == knots[0]) side = -2; else { side = -1; if (degree > 21) delta_t = free_delta_t = (double*)onmalloc(degree*sizeof(*delta_t)); } /* delta_t = {t - knot[order-2], t - knot[order-1], ... , t - knot[0]} */ knots += degree-1; if (side != -2) { k0=knots; k=degree; while(k--) *delta_t++ = t - *k0--; delta_t -= degree; cv += order*cv_stride; k = order; while (--k) { cv1 = cv; cv0 = cv1 - cv_stride; k0 = knots; /* *k0 = input_knots[d-1] */ k1 = k0+k; /* *k1 = input_knots[d-1+k] */ i = k; while(i--) { alpha1 = *delta_t++/(*k1-- - *k0--); alpha0 = 1.0 - alpha1; cv0 -= cv_inc; cv1 -= cv_inc; j = cv_dim; while (j--) {cv0--; cv1--; *cv1 = *cv0 * alpha0 + *cv1 * alpha1;} } delta_t -= k; } } else { dt = t - t0; // cv += order*cv_dim; // Chuck-n-Dale 21 Sep bug fix change cv_dim to cv_stride cv += order*cv_stride; k = order; while (--k) { cv1 = cv; cv0 = cv1 - cv_stride; k1 = knots+k; i = k; while(i--) { alpha1 = dt/(*k1-- - t0); alpha0 = 1.0 - alpha1; cv0 -= cv_inc; cv1 -= cv_inc; j = cv_dim; while (j--) {cv0--; cv1--; *cv1 = *cv0 * alpha0 + *cv1 * alpha1;} } } } } else { /* if left end of span is fully multiple and t = start knot, * then we're done. */ if (t == t0 && t0 == knots[0]) return true; /* if right end of span is fully multiple, save time */ if (side == 2) t1 = mult_k; else if (t1 == knots[2*degree-1]) side = 2; else { side = 1; if (degree > 21) delta_t = free_delta_t = (double*)onmalloc(degree*sizeof(*delta_t)); } knots += degree; if (side == 1) { /* variable right end knots * delta_t = {knot[order-1] - t, knot[order] - t, .. knot[2*order-3] - t} */ k1=knots; k = degree; while (k--) *delta_t++ = *k1++ - t; delta_t -= degree; k = order; while (--k) { cv0 = cv; cv1 = cv0 + cv_stride; k1 = knots; k0 = k1 - k; i = k; while(i--) { alpha0 = *delta_t++/(*k1++ - *k0++); alpha1 = 1.0 - alpha0; j = cv_dim; while(j--) {*cv0 = *cv0 * alpha0 + *cv1 * alpha1; cv0++; cv1++;} cv0 += cv_inc; cv1 += cv_inc; } delta_t -= k; } } else { /* all right end knots = t1 delta_t = t1 - t */ dt = t1 - t; k = order; while (--k) { cv0 = cv; cv1 = cv0 + cv_stride; k0 = knots - k; /* *knots = input_knots[d] */ i = k; while(i--) { alpha0 = dt/(t1 - *k0++); alpha1 = 1.0 - alpha0; j = cv_dim; while(j--) {*cv0 = *cv0 * alpha0 + *cv1 * alpha1; cv0++; cv1++;} cv0 += cv_inc; cv1 += cv_inc; } } } } if (free_delta_t) onfree(free_delta_t); return true; } bool ON_EvaluateNurbsBlossom(int cvdim, int order, int cv_stride, const double *CV,//size cv_stride*order const double *knot, //nondecreasing, size 2*(order-1) // knot[order-2] != knot[order-1] const double *t, //input parameters size order-1 double* P ) { if (!CV || !t || !knot) return false; if (cv_stride < cvdim) return false; const double* cv; int degree = order-1; double workspace[32]; double* space = workspace; double* free_space = nullptr; if (order > 32){ free_space = (double*)onmalloc(order*sizeof(double)); space = free_space; } int i, j, k; for (i=1; i<2*degree; i++){ if (knot[i] - knot[i-1] < 0.0) return false; } if (knot[degree] - knot[degree-1] < ON_EPSILON) return false; for (i=0; i= 1) * order * (>= 2) * cv * array of order*cvdim doubles that define the Nurb * span's control vertices * knot * array of (2*order - 2) doubles the define the Nurb * span's knot vector. The array should satisfiy * knot[0] <= ... <= knot[order-2] < knot[order-1] * <= ... <= knot[2*order-3] * t0, t1 * The portion of the Nurb span to convert to a Bezier. * (t0 < t1) * * OUTPUT: * TL_ConvNurbToBezier() * 0: successful * -1: failure * cv * Control vertices for the Bezier. * * COMMENTS: * If you want to convert the entire * span to a Bezier, set t0 = knots[order-2] and * t1 = knots[order-1]. * * If you want to extend the left end of the span a bit, * set t0 = knots[order-2] - a_bit and t1 = knots[order-1]. * * If you want to extend the right end of the span a bit, * set t0 = knots[order-2] and t1 = knots[order-1] + a_bit. * * EXAMPLE: * // ... * * REFERENCE: * BOHM-01, Page 7. * * RELATED FUNCTIONS: * TL_EvdeBoor(), TL_ConvertBezierToPolynomial */ { ON_EvaluateNurbsDeBoor(cvdim,order,cvstride,cv,knot, 1, 0.0, t0); ON_EvaluateNurbsDeBoor(cvdim,order,cvstride,cv,knot,-2, t0, t1); }