mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 03:26:09 +08:00
1458 lines
38 KiB
C++
1458 lines
38 KiB
C++
//
|
|
// Copyright (c) 1993-2022 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 <http://www.opennurbs.org>.
|
|
//
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
#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
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Computes tolerance associated with a generic evaluation domain
|
|
//
|
|
|
|
double ON_DomainTolerance( double a, double b )
|
|
{
|
|
if ( a == b )
|
|
return 0.0;
|
|
double tol = (fabs(a)+fabs(b)+fabs(a-b))* ON_SQRT_EPSILON;
|
|
if ( tol < ON_EPSILON )
|
|
tol = ON_EPSILON;
|
|
return tol;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Computes tolerance associated with knot[i]
|
|
//
|
|
|
|
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;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Computes tolerance associated with span of a knot vector
|
|
//
|
|
|
|
double ON_SpanTolerance( int order, int cv_count, const double* knot, int span_index )
|
|
{
|
|
const int i0 = span_index+order-2;
|
|
return ON_DomainTolerance( knot[i0], knot[i0+1] );
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Computes number of knots in knot vector
|
|
//
|
|
|
|
int ON_KnotCount( int order, int cv_count )
|
|
{
|
|
return (order+cv_count-2);
|
|
}
|
|
|
|
const ON_2dex ON_BsplineControlPointSpans(
|
|
int order,
|
|
int control_point_count,
|
|
int control_point_index
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (order < 2 || control_point_count < order)
|
|
break;
|
|
if (control_point_index < 0 || control_point_index >= control_point_count)
|
|
break;
|
|
const int i0 = control_point_index - order + 1;
|
|
const int i1 = control_point_index + 1;
|
|
const int span_count = control_point_count - order + 1;
|
|
return ON_2dex(i0 >= 0 ? i0 : 0, i1 <= span_count ? i1 : span_count);
|
|
}
|
|
return ON_2dex(0, 0);
|
|
}
|
|
|
|
ON_DECL const ON_Interval ON_BsplineControlPointSupport(int order, int control_point_count, const double* knots, int control_point_index)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == knots)
|
|
break;
|
|
const ON_2dex active_spans = ON_BsplineControlPointSpans(order, control_point_count, control_point_index);
|
|
if (active_spans.i < active_spans.j)
|
|
return ON_Interval(knots[active_spans.i + order - 2], knots[active_spans.j + order - 2]);
|
|
break;
|
|
}
|
|
return ON_Interval::Nan;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Computes number of knots in knot vector
|
|
//
|
|
|
|
int ON_KnotMultiplicity(
|
|
int order, // order (>=2)
|
|
int cv_count, // cv_count (>=order)
|
|
const double* knot, // knot[]
|
|
int knot_index // knot_index
|
|
)
|
|
{
|
|
int knot_count = order+cv_count-2;
|
|
int km = 0;
|
|
if ( knot && knot_index >= 0 && knot_index < knot_count ) {
|
|
while(knot_index > 0 && knot[knot_index] == knot[knot_index-1])
|
|
knot_index--;
|
|
knot += knot_index;
|
|
knot_count -= knot_index;
|
|
km = 1;
|
|
while ( km < knot_count && knot[0] == knot[km] )
|
|
km++;
|
|
}
|
|
return km;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Computes number of non-empty spans
|
|
//
|
|
|
|
int ON_KnotVectorSpanCount(
|
|
int order, // order (>=2)
|
|
int cv_count, // cv count
|
|
const double* knot // knot[] array
|
|
)
|
|
{
|
|
if ( 0 == knot )
|
|
{
|
|
if ( 0 != order || 0 != cv_count )
|
|
{
|
|
ON_ERROR("nullptr knot[] passed to ON_KnotVectorSpanCount.");
|
|
}
|
|
return 0;
|
|
}
|
|
int i, span_count = 0;
|
|
for ( i = order-1; i < cv_count; i++ ) {
|
|
if ( knot[i] > knot[i-1] )
|
|
span_count++;
|
|
}
|
|
return span_count;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Gets span vector from knot vector
|
|
//
|
|
|
|
bool ON_GetKnotVectorSpanVector(
|
|
int order, // order (>=2)
|
|
int cv_count, // cv count
|
|
const double* knot, // knot[] array
|
|
double* s // s[] array
|
|
)
|
|
{
|
|
if ( 0 == knot || 0 == s )
|
|
{
|
|
if ( 0 != order || 0 != cv_count )
|
|
{
|
|
ON_ERROR("nullptr knot[] or s[] passed to ON_KnotVectorSpanCount.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int i, span_count = 0;
|
|
s[span_count++] = knot[order-2];
|
|
for ( i = order-1; i < cv_count; i++ ) {
|
|
if ( knot[i] > knot[i-1] )
|
|
s[span_count++] = knot[i];
|
|
}
|
|
return (span_count>1) ? true : false;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Computes span index for evaluation of parameter
|
|
//
|
|
|
|
int ON_NurbsSpanIndex(
|
|
int order, // (>=2)
|
|
int cv_count,
|
|
const double* knot, // knot[] array or length ON_KnotCount(order,cv_count)
|
|
double t, // evaluation parameter
|
|
int side, // side 0 = default, -1 = from below, +1 = from above
|
|
int hint // hint (or 0 if no hint available)
|
|
)
|
|
{
|
|
int j, len;
|
|
|
|
// shift knot so that domain is knot[0] to knot[len]
|
|
knot += (order-2);
|
|
len = cv_count-order+2;
|
|
|
|
// see if hint helps
|
|
if (hint > 0 && hint < len-1) {
|
|
while(hint > 0 && knot[hint-1] == knot[hint]) hint--;
|
|
if (hint > 0) {
|
|
// have knot[hint-1] < knot[hint]
|
|
if (t < knot[hint]) {
|
|
len = hint+1;
|
|
hint = 0;
|
|
}
|
|
else {
|
|
if (side < 0 && t == knot[hint])
|
|
hint--;
|
|
knot += hint;
|
|
len -= hint;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
hint = 0;
|
|
|
|
j = ON_SearchMonotoneArray(knot,len,t);
|
|
if (j < 0)
|
|
j = 0;
|
|
else if (j >= len-1)
|
|
j = len-2;
|
|
else if (side < 0) {
|
|
// if user wants limit from below and t = an internal knot,
|
|
// back up to previous span
|
|
while(j > 0 && t == knot[j])
|
|
j--;
|
|
}
|
|
return (j + hint);
|
|
}
|
|
|
|
|
|
int ON_NextNurbsSpanIndex( int order, int cv_count, const double* knot, int span_index )
|
|
|
|
/*
|
|
Get index of next non-degenerate NURBS span
|
|
|
|
INPUT:
|
|
order, cv_count, knot
|
|
knot vector
|
|
span_index
|
|
current span index ( >= 0 and <= cv_count-order )
|
|
OUTPUT:
|
|
i = ON_NextNurbsSpanIndex()
|
|
i>=0: successful - the index of the next span or
|
|
cv_count-order if the input value
|
|
was cv_count-or
|
|
knot[i+order-2] < knot[i+order-1]
|
|
<0: failure
|
|
|
|
COMMENTS:
|
|
The first span in a NURBS has span_index = 0. The last span in a NURBS
|
|
has span_index = cv_count-order.
|
|
A span of a degree d NURBS is defined by d+1 CVs and 2*d knots. For a
|
|
given span_index, the associated knots and CVs are
|
|
{knot[span_index], ..., knot[span_index+2*d-1]}
|
|
and
|
|
{CV[span_index], ..., CV[span_index + d]}
|
|
The domain of the span is
|
|
[ knot[span_index+order-2], knot[span_index+order-1] ].
|
|
|
|
EXAMPLE:
|
|
// print the values of all distinct knots in a NURBS's domain
|
|
int span_index = 0;
|
|
int next_span_index = 0;
|
|
for (;;) {
|
|
printf( "knot[%2d] = %g\n", span_index+order-2, knot[span_index+order-2] );
|
|
next_span_index =ON_NextNurbsSpanIndex( order, cv_count, knot, span_index );
|
|
if ( next_span_index < 0 )
|
|
break; // illegal input
|
|
if ( next_span_index == span_index ) {
|
|
// end of the domain
|
|
printf( "knot[%2d] = %g\n", cv_count-1, knot[cv_count-1] );
|
|
break;
|
|
}
|
|
next_span_index = span_index;
|
|
}
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
if (span_index < 0 || span_index > cv_count-order || !knot)
|
|
return -1;
|
|
|
|
if ( span_index < cv_count-order ) {
|
|
do {
|
|
span_index++;
|
|
}
|
|
while ( span_index < cv_count-order &&
|
|
knot[span_index+order-2] == knot[span_index+order-1] );
|
|
}
|
|
return span_index;
|
|
}
|
|
|
|
|
|
int ON_GetSpanIndices(int order,
|
|
int cv_count,
|
|
const double* knot,
|
|
int* span_indices)
|
|
|
|
/* span_indices should have size greater than the number of
|
|
spans (cv_count is big enough).
|
|
|
|
returns span count.
|
|
fills in span_indices with index of last in each bunch of multiple knots at
|
|
start of span, and first in buch at end of nurb.
|
|
|
|
|
|
*/
|
|
{
|
|
int span_index, next_span_index, j;
|
|
|
|
span_index = -1;
|
|
next_span_index = 0;
|
|
j = 0;
|
|
while (span_index != next_span_index) {
|
|
span_index = next_span_index;
|
|
span_indices[j] = span_index + order - 2;
|
|
next_span_index = ON_NextNurbsSpanIndex(order, cv_count, knot, span_index);
|
|
if (next_span_index < 0)
|
|
return next_span_index;
|
|
j++;
|
|
}
|
|
|
|
span_indices[j] = span_index + order - 1;
|
|
|
|
return j;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Computes value for superfluous knot used in systems like OpenGL and 3dsMax
|
|
//
|
|
|
|
double ON_SuperfluousKnot(
|
|
int order, int cv_count, const double* knot,
|
|
int end )
|
|
{
|
|
double k;
|
|
const int knot_count = order+cv_count-2;
|
|
// gets superfluous knot for translation to other formats
|
|
k = knot[(end) ? knot_count-1 : 0];
|
|
if (order > 2 && cv_count >= 2*order - 2 && cv_count >= 6 ) {
|
|
// check for non-clamped knots
|
|
if (end) {
|
|
if ( knot[cv_count-1] < knot[knot_count-1] )
|
|
k += (knot[order+1] - knot[order]);
|
|
}
|
|
else {
|
|
if ( knot[0] < knot[order-2] )
|
|
k -= (knot[cv_count-order+1] - knot[cv_count-order]);
|
|
}
|
|
}
|
|
return k;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Used to determine when a knot vector is periodic
|
|
//
|
|
|
|
bool ON_IsKnotVectorPeriodic(
|
|
int order,
|
|
int cv_count,
|
|
const double* knot
|
|
)
|
|
|
|
{
|
|
double tol;
|
|
const double* k1;
|
|
int i;
|
|
|
|
if ( order < 2 || cv_count < order || !knot ) {
|
|
ON_ERROR("ON_IsKnotVectorPeriodic(): illegal input");
|
|
return false;
|
|
}
|
|
|
|
if ( order == 2 )
|
|
return false; // convention is that degree 1 curves cannot be periodic.
|
|
|
|
if (order <= 4) {
|
|
if (cv_count < order+2)
|
|
return false;
|
|
}
|
|
else if ( cv_count < 2*order-2 ) {
|
|
return false;
|
|
}
|
|
|
|
tol = fabs(knot[order-1] - knot[order-3])* ON_SQRT_EPSILON;
|
|
if (tol < fabs(knot[cv_count-1] - knot[order-2])* ON_SQRT_EPSILON)
|
|
tol = fabs(knot[cv_count-1] - knot[order-2])* ON_SQRT_EPSILON;
|
|
k1 = knot+cv_count-order+1;
|
|
i = 2*(order-2);
|
|
while(i--) {
|
|
if (fabs(knot[1] - knot[0] + k1[0] - k1[1]) > tol)
|
|
return false;
|
|
knot++; k1++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Used to determine when a knot vector is clamped
|
|
//
|
|
|
|
bool ON_IsKnotVectorClamped(
|
|
int order,
|
|
int cv_count,
|
|
const double* knot,
|
|
int end // (default = 2) 0 = left end, 1 = right end, 2 = both
|
|
)
|
|
|
|
{
|
|
if ( order <= 1 || cv_count < order || !knot || end < 0 || end > 2 )
|
|
return false;
|
|
bool rc = true;
|
|
if ( (end == 0 || end == 2) && knot[0] != knot[order-2] )
|
|
rc = false;
|
|
if ( (end == 1 || end == 2) && knot[cv_count-1] != knot[order+cv_count-3] )
|
|
rc = false;
|
|
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];
|
|
rc = (0.0 != delta && delta > ON_UNSET_VALUE && delta < ON_UNSET_POSITIVE_VALUE);
|
|
if (rc)
|
|
{
|
|
const int i0 = ON_IsKnotVectorClamped(order, cv_count, knot, 0) ? order : 1;
|
|
const int i1 = ON_IsKnotVectorClamped(order, cv_count, knot, 1) ? cv_count : ON_KnotCount(order, cv_count);
|
|
double k0 = knot[i0 - 1];
|
|
const double delta_tol = fabs(ON_SQRT_EPSILON*delta);
|
|
for (int i = i0; i < i1 && rc; ++i)
|
|
{
|
|
const double d = knot[i] - k0;
|
|
rc = fabs(d - delta) <= delta_tol;
|
|
k0 = knot[i];
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Used to determine properties of knot vector
|
|
//
|
|
|
|
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;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Used to determine properties of knot vector
|
|
//
|
|
|
|
ON::knot_style ON_KnotVectorStyle(
|
|
int order,
|
|
int cv_count,
|
|
const double* knot
|
|
)
|
|
{
|
|
ON::knot_style s = ON::unknown_knot_style;
|
|
if ( order >= 2 && cv_count >= order && knot && knot[order-2] < knot[cv_count-1] ) {
|
|
const int knot_count = order+cv_count-2;
|
|
const double delta = 0.5*((knot[order-1] - knot[order-2]) + (knot[cv_count-1] - knot[cv_count-2]));
|
|
const double ktol = delta*1.0e-6;
|
|
int i;
|
|
if ( ON_IsKnotVectorClamped( order, cv_count, knot ) ) {
|
|
if ( order == cv_count ) {
|
|
s = ON::piecewise_bezier_knots;
|
|
}
|
|
else {
|
|
s = ON::clamped_end_knots;
|
|
for ( i = order-1; i <= cv_count-1; i++ ) {
|
|
if ( fabs(knot[i] - knot[i-1] - delta) > ktol ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( i >= cv_count ) {
|
|
s = ON::quasi_uniform_knots;
|
|
}
|
|
else {
|
|
const int degree = order-1;
|
|
for ( i = order-1; i < cv_count-1; i += degree ) {
|
|
if ( knot[i] != knot[i+degree-1] )
|
|
break;
|
|
}
|
|
if ( i >= cv_count-1 )
|
|
s = ON::piecewise_bezier_knots;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// check for uniform knots
|
|
s = ON::non_uniform_knots;
|
|
for ( i = 1; i < knot_count; i++ ) {
|
|
if ( fabs(knot[i] - knot[i-1] - delta) > ktol ) {
|
|
break;
|
|
}
|
|
}
|
|
if ( i >= knot_count )
|
|
s = ON::uniform_knots;
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Used to set the domain of a knot vector
|
|
//
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Used to get the domain of a knot vector
|
|
//
|
|
|
|
bool ON_GetKnotVectorDomain(
|
|
int order,
|
|
int cv_count,
|
|
const double* knot,
|
|
double* k0, double* k1
|
|
)
|
|
{
|
|
if ( order < 2 || cv_count < order || knot == nullptr )
|
|
return false;
|
|
if ( k0 )
|
|
*k0 = knot[order-2];
|
|
if ( k1 )
|
|
*k1 = knot[cv_count-1];
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Used to reverse knot vectors
|
|
//
|
|
|
|
bool ON_ReverseKnotVector(
|
|
int order,
|
|
int cv_count,
|
|
double* knot
|
|
)
|
|
{
|
|
if ( order < 2 || cv_count < order || knot == nullptr )
|
|
return false;
|
|
const int knot_count = (order+cv_count-2);
|
|
double t;
|
|
int i, j;
|
|
for ( i = 0, j = knot_count-1; i <= j; i++, j-- ) {
|
|
t = knot[i];
|
|
knot[i] = -knot[j];
|
|
knot[j] = -t;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Used to compare knot vectors
|
|
//
|
|
|
|
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;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
//
|
|
// Used to validate knot vectors
|
|
//
|
|
|
|
static bool ON_KnotVectorIsNotValid(bool bSilentError)
|
|
{
|
|
return bSilentError ? false : ON_IsNotValid(); // <-- good place for a breakpoint
|
|
}
|
|
|
|
bool ON_IsValidKnotVector( int order, int cv_count, const double* knot, ON_TextLog* text_logx )
|
|
{
|
|
// If low bit of text_log pointer is 1, then ON_Error is not called when the
|
|
// knot vector is invalid.
|
|
const ON__INT_PTR lowbit = 1;
|
|
const ON__INT_PTR hightbits = ~lowbit;
|
|
bool bSilentError = ( 0 != (lowbit & ((ON__INT_PTR)text_logx)) );
|
|
ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits);
|
|
|
|
const double *k0, *k1;
|
|
int i;
|
|
if ( order < 2 )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("Knot vector order = %d (should be >= 2 )\n",order);
|
|
}
|
|
return ON_KnotVectorIsNotValid(bSilentError);
|
|
}
|
|
if ( cv_count < order )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("Knot vector cv_count = %d (should be >= order=%d )\n",cv_count,order);
|
|
}
|
|
return ON_KnotVectorIsNotValid(bSilentError);
|
|
}
|
|
if ( knot == nullptr )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("Knot vector knot array = nullptr.\n");
|
|
}
|
|
return ON_KnotVectorIsNotValid(bSilentError);
|
|
}
|
|
|
|
for ( i = 0; i < cv_count+order-2; i++ )
|
|
{
|
|
if ( !ON_IsValid(knot[i]) )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("Knot vector knot[%d]=%g is not valid.\n",i,knot[i]);
|
|
}
|
|
return ON_KnotVectorIsNotValid(bSilentError);
|
|
}
|
|
}
|
|
|
|
if ( !(knot[order-2] < knot[order-1]) )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("Knot vector order=%d and knot[%d]=%g >= knot[%d]=%g (should have knot[order-2] < knot[order-1]).\n",
|
|
order,order-2,knot[order-2],order-1,knot[order-1]);
|
|
}
|
|
return ON_KnotVectorIsNotValid(bSilentError);
|
|
}
|
|
if ( !(knot[cv_count-2] < knot[cv_count-1]) )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("Knot vector cv_count=%d and knot[%d]=%g >= knot[%d]=%g (should have knot[cv_count-2] < knot[cv_count-1]).\n",
|
|
cv_count,cv_count-2,knot[cv_count-2],cv_count-1,knot[cv_count-1]);
|
|
}
|
|
return ON_KnotVectorIsNotValid(bSilentError);
|
|
}
|
|
|
|
// entire array must be monotone increasing
|
|
k0 = knot;
|
|
k1 = knot+1;
|
|
i = order + cv_count - 3;
|
|
while (i--) {
|
|
if ( !(*k1 >= *k0) )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("Knot vector must be increasing but knot[%d]=%g > knot[%d]=%g\n",
|
|
order+cv_count-4-i, *k0, order+cv_count-3-i, *k1 );
|
|
}
|
|
return ON_KnotVectorIsNotValid(bSilentError);
|
|
}
|
|
k0++;
|
|
k1++;
|
|
}
|
|
|
|
// must have knot[i+order-1] > knot[i]
|
|
k0 = knot;
|
|
k1 = knot + order - 1;
|
|
i = cv_count-1;
|
|
while(i--) {
|
|
if ( !(*k1 > *k0) )
|
|
{
|
|
if ( text_log )
|
|
{
|
|
text_log->Print("Knot vector order = %d but knot[%d]=%g >= knot[%d]=%g\n",
|
|
order, cv_count-2-i, *k0, cv_count-1-i, *k1 );
|
|
}
|
|
return ON_KnotVectorIsNotValid(bSilentError);
|
|
}
|
|
k0++;
|
|
k1++;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
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_MakeKnotVectorPeriodic(
|
|
int order, // order (>=2)
|
|
int cv_count, // cv count (>= (order>=4) ? 2*(order-1) : 5)
|
|
double* knot // knot[] array
|
|
)
|
|
{
|
|
double *k0, *k1;
|
|
int i;
|
|
|
|
if ( order < 2 || cv_count < order || !knot ) {
|
|
ON_ERROR("ON_MakePeriodicKnotVector(): illegal input");
|
|
return false;
|
|
}
|
|
|
|
switch(order) {
|
|
case 2:
|
|
if ( cv_count < 4 ) {
|
|
ON_ERROR("ON_MakePeriodicKnotVector(): illegal input degree=1, cv_count<4");
|
|
return false;
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
break;
|
|
case 3:
|
|
if ( cv_count < 4 ) {
|
|
ON_ERROR("ON_MakePeriodicKnotVector(): illegal input degree=2, cv_count<5");
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
if ( cv_count < 2*order-2 ) {
|
|
ON_ERROR("ON_MakePeriodicKnotVector(): illegal input degree>=3, cv_count<2*degree");
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
k0 = knot + order-2;
|
|
k1 = knot + cv_count-1;
|
|
i = order-2;
|
|
while (i--) {
|
|
k1[1] = k0[1]-k0[0]+k1[0];
|
|
k0++; k1++;
|
|
}
|
|
k0 = knot + order-2;
|
|
k1 = knot + cv_count-1;
|
|
i = order-2;
|
|
while (i--) {
|
|
k0[-1] = k1[-1] - k1[0] + k0[0];
|
|
k0--;
|
|
k1--;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
ON_DECL
|
|
bool ON_MakeClampedUniformKnotVector(
|
|
int order,
|
|
int cv_count,
|
|
double* knot,
|
|
double delta
|
|
)
|
|
{
|
|
bool rc = false;
|
|
if ( order >= 2 && cv_count >= order && knot != nullptr && delta > 0.0 )
|
|
{
|
|
double k;
|
|
int i;
|
|
for ( i = order-2, k = 0.0; i < cv_count; i++, k += delta )
|
|
knot[i] = k;
|
|
ON_ClampKnotVector(order,cv_count,knot,2);
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
// 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 != nullptr && 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;
|
|
}
|
|
|
|
|
|
|
|
double ON_GrevilleAbcissa( // get Greville abcissa
|
|
int order, // order (>=2)
|
|
const double* knot // knot[order-1] array
|
|
)
|
|
|
|
{
|
|
double g=0.0;
|
|
if ( order <= 2 || knot[0] == knot[order-2])
|
|
{
|
|
g = knot[0]; // degree = 1 or fully multiple knot
|
|
}
|
|
else
|
|
{
|
|
// g = (knot[i]+...+knot[i+degree-1])/degree
|
|
order--;
|
|
const double k0 = knot[0];
|
|
const double k = knot[order/2];
|
|
const double k1 = knot[order-1];
|
|
const double tol = (k1-k0)*ON_SQRT_EPSILON;
|
|
const double const_ord = (double)order;
|
|
while ( order--)
|
|
{
|
|
g += *knot++;
|
|
}
|
|
//g /= ((double)order);
|
|
g /= const_ord;
|
|
if ( fabs(2*k - (k0+k1)) <= tol && fabs(g-k) <= (fabs(g)*ON_SQRT_EPSILON+tol) )
|
|
g = k; // sets g to exact value when knot vector is uniform
|
|
}
|
|
return g;
|
|
}
|
|
|
|
bool ON_GetGrevilleAbcissae( // get Greville abcissa from knots
|
|
int order, // order (>=2)
|
|
int cv_count, // cv count (>=order)
|
|
const double* knot, // knot[] array
|
|
bool bPeriodic,
|
|
double* g // has length cv_count in non-periodic case
|
|
// and length cv_count-order+1 in periodic case
|
|
)
|
|
{
|
|
// Grevielle abscissae for a given knot vector
|
|
if ( order < 2 || cv_count < order || !knot || !g )
|
|
return false;
|
|
|
|
const int g_count = (bPeriodic) ? cv_count-order+1 : cv_count;
|
|
|
|
if (order == 2)
|
|
{
|
|
// g[i] = knot[i] in degree 1 case
|
|
for (int i = 0; i < g_count; i++)
|
|
g[i] = knot[i];
|
|
}
|
|
else
|
|
{
|
|
// g = (knot[i]+...+knot[i+degree-1])/degree
|
|
const double t0 = knot[order-2];
|
|
if (bPeriodic)
|
|
{
|
|
for (int i = 0; i < order - 1; ++i)
|
|
{
|
|
g[i] = ON_GrevilleAbcissa(order, knot + i);
|
|
if (g[i] >= t0)
|
|
{
|
|
knot += ((i > 0 && (t0 - g[i - 1]) < (g[i] - t0)) ? (i - 1) : i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
for ( int i = 0; i < g_count; ++i)
|
|
g[i] = ON_GrevilleAbcissa( order, knot+i );
|
|
if (bPeriodic && g[0] < t0)
|
|
g[0] = t0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
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 = nullptr;
|
|
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;
|
|
}
|
|
|
|
bool ON_ClampKnotVector(
|
|
int cv_dim, // dimension of cv's = ( = dim+1 for rational cvs )
|
|
int order,
|
|
int cv_count,
|
|
int cv_stride,
|
|
double* cv, // nullptr 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 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]
|
|
// Adjusts initial/final order many CVs so that the curve location is unchanged.
|
|
// Requires that knot[order-2]< knot[order-1] and/or knot[cv_count-2] < knot[cv_count-1]
|
|
// 17-June-2020 Improved error reporting.
|
|
bool rc = false;
|
|
int i, i0;
|
|
|
|
if (cv && knot && order >= 2 && cv_count >= order && end>=0 && end<=2 ) {
|
|
rc = true;
|
|
if ( end == 0 || end == 2 ) {
|
|
if (ON_EvaluateNurbsDeBoor(cv_dim, order, cv_stride, cv, knot, 1, 0.0, knot[order - 2]))
|
|
{
|
|
for (i = 0; i < order - 2; i++)
|
|
knot[i] = knot[order - 2];
|
|
}
|
|
else
|
|
rc = false;
|
|
}
|
|
if ( end == 1 || end == 2 ) {
|
|
i0 = cv_count-order;
|
|
knot += i0;
|
|
cv += i0*cv_stride;
|
|
if (ON_EvaluateNurbsDeBoor(cv_dim, order, cv_stride, cv, knot, -1, 0.0, knot[order - 1]))
|
|
{
|
|
i0 = order - 1;
|
|
for (i = 2 * order - 3; i > i0; i--)
|
|
knot[i] = knot[i0];
|
|
}
|
|
else
|
|
rc = false;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
static bool ON_InsertSingleKnot( int cv_dim, int order,
|
|
int cv_stride,
|
|
double *cv, // nullptr or array of length at least order*cv_stride+cv_dim
|
|
double *knot, // array of length at least 2*order-1 and existing knot values in
|
|
// knot[0], ..., knot[2*order-3]
|
|
double knot_value // knot[order-2] <= knot_value < knot[order-1]
|
|
// and knot[0] < knot_vale
|
|
)
|
|
{
|
|
double alpha0, alpha1;
|
|
double *k0, *k1, *prev_cv;
|
|
int i, d, cv_inc, degree;
|
|
|
|
if ( order < 2 || !knot || knot_value < knot[order-2] || knot[order-1] <= knot_value ) {
|
|
ON_ERROR( "ON_InsertSingleKnot() - illegal knot input" );
|
|
return false;
|
|
}
|
|
|
|
if ( cv ) {
|
|
if ( cv_dim < 1 || cv_stride < cv_dim ) {
|
|
ON_ERROR( "ON_InsertSingleKnot() - illegal cv input" );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
degree = order-1;
|
|
|
|
// move last degree many knots over one spot
|
|
k1 = knot + 2*degree;
|
|
k0 = k1-1;
|
|
i = degree;
|
|
while (i--)
|
|
*k1-- = *k0--;
|
|
|
|
// insert new knot value
|
|
*k1 = knot_value;
|
|
|
|
if ( cv ) {
|
|
// move last cv over one spot
|
|
memcpy( cv+cv_dim*order, cv+cv_dim*degree, cv_dim*sizeof(*cv) );
|
|
|
|
// compute new cv values
|
|
k0 = knot + degree-1;
|
|
k1 = k0 + order;
|
|
cv += order*cv_stride;
|
|
prev_cv = cv - cv_stride;
|
|
cv_inc = cv_stride - cv_dim;
|
|
i = degree;
|
|
if (knot_value - *k0 <= *k1 - knot_value) {
|
|
while (i--) {
|
|
alpha1 = (knot_value - *k0)/(*k1 - *k0);
|
|
alpha0 = 1.0 - alpha1;
|
|
k0--; k1--;
|
|
cv -= cv_inc;
|
|
prev_cv -= cv_inc;
|
|
d = cv_dim;
|
|
while (d--) {
|
|
--cv;
|
|
--prev_cv;
|
|
*cv = *cv * alpha1 + *prev_cv * alpha0;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
while (i--) {
|
|
alpha0 = (*k1 - knot_value)/(*k1 - *k0);
|
|
alpha1 = 1.0 - alpha0;
|
|
k0--; k1--;
|
|
cv -= cv_inc;
|
|
prev_cv -= cv_inc;
|
|
d = cv_dim;
|
|
while (d--) {
|
|
--cv;
|
|
--prev_cv;
|
|
*cv = *cv * alpha1 + *prev_cv * alpha0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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, // nullptr 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 nullptr 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_local = (cv_count-order);
|
|
while (j_local--)
|
|
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_local = cv_count-order;
|
|
while (j_local--) {
|
|
memcpy( cv+i1, cv+i0, cv_dim*sizeof(*cv) );
|
|
i1 -= cv_stride;
|
|
i0 -= cv_stride;
|
|
}
|
|
|
|
// update cv values
|
|
for ( j_local = 0; j_local < order+rc; j_local++ ) {
|
|
memcpy( cv, new_cv, cv_dim*sizeof(*new_cv) );
|
|
cv += cv_stride;
|
|
new_cv += cv_dim;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
onfree(new_knot);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
|