1385 lines
38 KiB
C
1385 lines
38 KiB
C
/*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2016 Marcel Steinbeck
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
#include "tinyspline.h"
|
|
|
|
#include <stdlib.h> /* malloc, free */
|
|
#include <math.h> /* fabs, sqrt */
|
|
#include <string.h> /* memcpy, memmove, strcmp */
|
|
#include <setjmp.h> /* setjmp, longjmp */
|
|
|
|
|
|
/********************************************************
|
|
* *
|
|
* Error handling *
|
|
* *
|
|
********************************************************/
|
|
#define TRY( x, y ) y = (tsError) setjmp( x ); if( y == 0 ) {
|
|
#define CATCH } \
|
|
else {
|
|
#define ETRY }
|
|
|
|
|
|
/********************************************************
|
|
* *
|
|
* Internal functions *
|
|
* *
|
|
********************************************************/
|
|
void ts_internal_deboornet_copy( const tsDeBoorNet* original,
|
|
tsDeBoorNet* copy, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t dim = original->dim;
|
|
const size_t n_points = original->n_points;
|
|
const size_t sof_f = sizeof(tsReal);
|
|
const size_t sof_p = n_points * dim * sof_f;
|
|
|
|
if( original == copy )
|
|
return;
|
|
|
|
copy->u = original->u;
|
|
copy->k = original->k;
|
|
copy->s = original->s;
|
|
copy->h = original->h;
|
|
copy->dim = dim;
|
|
copy->n_points = n_points;
|
|
copy->points = (tsReal*) malloc( sof_p );
|
|
|
|
if( copy->points == NULL )
|
|
longjmp( buf, TS_MALLOC );
|
|
|
|
memcpy( copy->points, original->points, sof_p );
|
|
copy->result = copy->points + (n_points - 1) * dim;
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_find_u( const tsBSpline* bspline, const tsReal u,
|
|
size_t* k, size_t* s, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t deg = bspline->deg;
|
|
const size_t order = bspline->order;
|
|
const size_t n_knots = bspline->n_knots;
|
|
|
|
*k = *s = 0;
|
|
|
|
for( ; *k < n_knots; (*k)++ )
|
|
{
|
|
const tsReal uk = bspline->knots[*k];
|
|
|
|
if( ts_fequals( u, uk ) )
|
|
{
|
|
(*s)++;
|
|
}
|
|
else if( u < uk )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* keep in mind that currently k is k+1 */
|
|
if( *s > order )
|
|
longjmp( buf, TS_MULTIPLICITY );
|
|
|
|
if( *k <= deg ) /* u < u_min */
|
|
longjmp( buf, TS_U_UNDEFINED );
|
|
|
|
if( *k == n_knots && *s == 0 ) /* u > u_last */
|
|
longjmp( buf, TS_U_UNDEFINED );
|
|
|
|
if( *k > n_knots - deg + *s - 1 ) /* u > u_max */
|
|
longjmp( buf, TS_U_UNDEFINED );
|
|
|
|
(*k)--; /* k+1 - 1 will never underflow */
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_copy( const tsBSpline* original,
|
|
tsBSpline* copy, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t dim = original->dim;
|
|
const size_t sof_f = sizeof(tsReal);
|
|
const size_t n_ctrlp = original->n_ctrlp;
|
|
const size_t n_knots = original->n_knots;
|
|
const size_t sof_ck = (n_ctrlp * dim + n_knots) * sof_f;
|
|
|
|
/* Nothing to do here. */
|
|
if( original == copy )
|
|
return;
|
|
|
|
copy->deg = original->deg;
|
|
copy->order = original->order;
|
|
copy->dim = original->dim;
|
|
copy->n_ctrlp = original->n_ctrlp;
|
|
copy->n_knots = original->n_knots;
|
|
copy->ctrlp = (tsReal*) malloc( sof_ck );
|
|
|
|
if( copy->ctrlp == NULL )
|
|
longjmp( buf, TS_MALLOC );
|
|
|
|
memcpy( copy->ctrlp, original->ctrlp, sof_ck );
|
|
copy->knots = copy->ctrlp + n_ctrlp * dim;
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_fill_knots( const tsBSpline* original, const tsBSplineType type,
|
|
const tsReal min, const tsReal max,
|
|
tsBSpline* result, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t n_knots = original->n_knots;
|
|
const size_t deg = original->deg;
|
|
const size_t order = deg + 1; /* Using deg+1 instead of original->order
|
|
* ensures order >= 1. */
|
|
tsReal fac; /* The factor used to calculate the knot values. */
|
|
size_t i; /* Used in for loops. */
|
|
|
|
/* order >= 1 implies 2*order >= 2 implies n_knots >= 2 */
|
|
if( n_knots < 2 * order )
|
|
longjmp( buf, TS_DEG_GE_NCTRLP );
|
|
|
|
if( type == TS_BEZIERS && n_knots % order != 0 )
|
|
longjmp( buf, TS_NUM_KNOTS );
|
|
|
|
if( min > max || ts_fequals( min, max ) )
|
|
longjmp( buf, TS_KNOTS_DECR );
|
|
|
|
/* copy spline even if type is TS_NONE */
|
|
ts_internal_bspline_copy( original, result, buf );
|
|
|
|
if( type == TS_OPENED )
|
|
{
|
|
/* ensures that the first knot value is exactly \min */
|
|
result->knots[0] = min; /* n_knots >= 2 */
|
|
|
|
fac = (max - min) / (n_knots - 1); /* n_knots >= 2 */
|
|
|
|
for( i = 1; i < n_knots - 1; i++ )
|
|
result->knots[i] = min + i * fac;
|
|
|
|
/* ensure that the last knot value is exactly \max */
|
|
result->knots[i] = max;
|
|
}
|
|
else if( type == TS_CLAMPED )
|
|
{
|
|
/* n_knots >= 2*order == 2*(deg+1) == 2*deg + 2 > 2*deg - 1 */
|
|
fac = (max - min) / (n_knots - 2 * deg - 1);
|
|
|
|
ts_arr_fill( result->knots, order, min );
|
|
|
|
for( i = order; i < n_knots - order; i++ )
|
|
result->knots[i] = min + (i - deg) * fac;
|
|
|
|
ts_arr_fill( result->knots + i, order, max );
|
|
}
|
|
else if( type == TS_BEZIERS )
|
|
{
|
|
/* n_knots >= 2*order implies n_knots/order >= 2 */
|
|
fac = (max - min) / (n_knots / order - 1);
|
|
|
|
ts_arr_fill( result->knots, order, min );
|
|
|
|
for( i = order; i < n_knots - order; i += order )
|
|
ts_arr_fill( result->knots + i, order, min + (i / order) * fac );
|
|
|
|
ts_arr_fill( result->knots + i, order, max );
|
|
}
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_new( const size_t n_ctrlp, const size_t dim, const size_t deg,
|
|
const tsBSplineType type, tsBSpline* bspline, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t order = deg + 1;
|
|
const size_t n_knots = n_ctrlp + order;
|
|
const size_t sof_f = sizeof(tsReal);
|
|
const size_t sof_ck = (n_ctrlp * dim + n_knots) * sof_f;
|
|
tsError e;
|
|
jmp_buf b;
|
|
|
|
if( dim < 1 )
|
|
longjmp( buf, TS_DIM_ZERO );
|
|
|
|
if( deg >= n_ctrlp )
|
|
longjmp( buf, TS_DEG_GE_NCTRLP );
|
|
|
|
bspline->deg = deg;
|
|
bspline->order = order;
|
|
bspline->dim = dim;
|
|
bspline->n_ctrlp = n_ctrlp;
|
|
bspline->n_knots = n_knots;
|
|
bspline->ctrlp = (tsReal*) malloc( sof_ck );
|
|
|
|
if( bspline->ctrlp == NULL )
|
|
longjmp( buf, TS_MALLOC );
|
|
|
|
bspline->knots = bspline->ctrlp + n_ctrlp * dim;
|
|
|
|
TRY( b, e )
|
|
ts_internal_bspline_fill_knots( bspline, type, 0.f, 1.f, bspline, b );
|
|
CATCH free( bspline->ctrlp );
|
|
|
|
longjmp( buf, e );
|
|
ETRY
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_resize( const tsBSpline* bspline, const int n, const int back,
|
|
tsBSpline* resized, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t deg = bspline->deg;
|
|
const size_t dim = bspline->dim;
|
|
const size_t sof_f = sizeof(tsReal);
|
|
const size_t sof_c = dim * sof_f;
|
|
|
|
const size_t n_ctrlp = bspline->n_ctrlp;
|
|
const size_t n_knots = bspline->n_knots;
|
|
const size_t nn_ctrlp = n_ctrlp + n; /* The new length of ctrlp. */
|
|
const size_t nn_knots = n_knots + n; /* The new length of knots. */
|
|
const size_t sof_ncnk = (nn_ctrlp * dim + nn_knots) * sof_f;
|
|
const size_t min_n_ctrlp = n < 0 ? nn_ctrlp : n_ctrlp; /* The minimum of
|
|
* the control points old and new size. */
|
|
const size_t min_n_knots = n < 0 ? nn_knots : n_knots; /* the minimum of
|
|
* the knots old and new size. */
|
|
|
|
tsReal* from_ctrlp = bspline->ctrlp;
|
|
tsReal* from_knots = bspline->knots;
|
|
tsReal* to_ctrlp = NULL;
|
|
tsReal* to_knots = NULL;
|
|
|
|
/* If n is 0 the spline must not be resized. */
|
|
if( n == 0 )
|
|
{
|
|
ts_internal_bspline_copy( bspline, resized, buf );
|
|
return;
|
|
}
|
|
|
|
if( bspline != resized )
|
|
{
|
|
ts_internal_bspline_new( nn_ctrlp, dim, deg, TS_NONE, resized, buf );
|
|
to_ctrlp = resized->ctrlp;
|
|
to_knots = resized->knots;
|
|
}
|
|
else
|
|
{
|
|
if( nn_ctrlp <= deg )
|
|
longjmp( buf, TS_DEG_GE_NCTRLP );
|
|
|
|
to_ctrlp = (tsReal*) malloc( sof_ncnk );
|
|
|
|
if( to_ctrlp == NULL )
|
|
longjmp( buf, TS_MALLOC );
|
|
|
|
to_knots = to_ctrlp + nn_ctrlp * dim;
|
|
}
|
|
|
|
/* Copy control points and knots. */
|
|
if( !back && n < 0 )
|
|
{
|
|
memcpy( to_ctrlp, from_ctrlp - n * dim, min_n_ctrlp * sof_c );
|
|
memcpy( to_knots, from_knots - n, min_n_knots * sof_f );
|
|
}
|
|
else if( !back && n > 0 )
|
|
{
|
|
memcpy( to_ctrlp + n * dim, from_ctrlp, min_n_ctrlp * sof_c );
|
|
memcpy( to_knots + n, from_knots, min_n_knots * sof_f );
|
|
}
|
|
else
|
|
{
|
|
/* n != 0 implies back == true */
|
|
memcpy( to_ctrlp, from_ctrlp, min_n_ctrlp * sof_c );
|
|
memcpy( to_knots, from_knots, min_n_knots * sof_f );
|
|
}
|
|
|
|
/* Cleanup if necessary. */
|
|
if( bspline == resized )
|
|
{
|
|
/* free old memory */
|
|
free( from_ctrlp );
|
|
/* assign new values */
|
|
resized->ctrlp = to_ctrlp;
|
|
resized->knots = to_knots;
|
|
resized->n_ctrlp = nn_ctrlp;
|
|
resized->n_knots = nn_knots;
|
|
}
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_insert_knot( const tsBSpline* bspline,
|
|
const tsDeBoorNet* deBoorNet,
|
|
const size_t n,
|
|
tsBSpline* result,
|
|
jmp_buf buf
|
|
)
|
|
{
|
|
const size_t deg = bspline->deg;
|
|
const size_t dim = bspline->dim;
|
|
const size_t k = deBoorNet->k;
|
|
const size_t sof_f = sizeof(tsReal);
|
|
const size_t sof_c = dim * sof_f;
|
|
size_t N; /* The number of affected control points. */
|
|
tsReal* from; /* The pointer to copy the values from. */
|
|
tsReal* to; /* The pointer to copy the values to. */
|
|
int stride; /* The stride of the next pointer to copy. Will be negative
|
|
* later on, thus use int. */
|
|
size_t i; /* Used in for loops. */
|
|
|
|
if( deBoorNet->s + n > bspline->order )
|
|
{
|
|
longjmp( buf, TS_MULTIPLICITY );
|
|
}
|
|
|
|
/* Use ::ts_bspline_resize even if \n is 0 to copy
|
|
* the spline if necessary. */
|
|
ts_internal_bspline_resize( bspline, (int) n, 1, result, buf );
|
|
|
|
if( n == 0 ) /* Nothing to insert. */
|
|
return;
|
|
|
|
N = deBoorNet->h + 1; /* n > 0 implies s <= deg implies a regular evaluation
|
|
* implies h+1 is valid. */
|
|
|
|
/* 1. Copy all necessary control points and knots from
|
|
* the original B-Spline.
|
|
* 2. Copy all necessary control points and knots from
|
|
* the de Boor net. */
|
|
|
|
/* 1.
|
|
*
|
|
* a) Copy left hand side control points from original b-spline.
|
|
* b) Copy right hand side control points from original b-spline.
|
|
* c) Copy left hand side knots from original b-spline.
|
|
* d) Copy right hand side knots form original b-spline. */
|
|
/* copy control points */
|
|
memmove( result->ctrlp, bspline->ctrlp, (k - deg) * sof_c ); /* a) */
|
|
from = bspline->ctrlp + dim * (k - deg + N);
|
|
to = result->ctrlp + dim * (k - deg + N + n); /* n >= 0 implies to >= from */
|
|
memmove( to, from, ( result->n_ctrlp - n - (k - deg + N) ) * sof_c ); /* b) */
|
|
/* copy knots */
|
|
memmove( result->knots, bspline->knots, (k + 1) * sof_f ); /* c) */
|
|
from = bspline->knots + k + 1;
|
|
to = result->knots + k + 1 + n; /* n >= 0 implies to >= from */
|
|
memmove( to, from, ( result->n_knots - n - (k + 1) ) * sof_f ); /* d) */
|
|
|
|
/* 2.
|
|
*
|
|
* a) Copy left hand side control points from de boor net.
|
|
* b) Copy middle part control points from de boor net.
|
|
* c) Copy right hand side control points from de boor net.
|
|
* d) Insert knots with u_k. */
|
|
from = deBoorNet->points;
|
|
to = result->ctrlp + (k - deg) * dim;
|
|
stride = (int) (N * dim);
|
|
|
|
/* copy control points */
|
|
for( i = 0; i < n; i++ ) /* a) */
|
|
{
|
|
memcpy( to, from, sof_c );
|
|
from += stride;
|
|
to += dim;
|
|
stride -= (int) dim;
|
|
}
|
|
|
|
memcpy( to, from, (N - n) * sof_c ); /* b) */
|
|
|
|
from -= dim;
|
|
to += (N - n) * dim;
|
|
stride = -(int) (N - n + 1) * (int) dim; /* N = h+1 with h = deg-s
|
|
* (ts_internal_bspline_evaluate) implies N = deg-s+1 = order-s.
|
|
* n <= order-s implies N-n+1 >= order-s - order-s + 1 = 1. Thus,
|
|
* -(int)(N-n+1) <= -1. */
|
|
|
|
for( i = 0; i < n; i++ ) /* c) */
|
|
{
|
|
memcpy( to, from, sof_c );
|
|
from += stride;
|
|
stride -= (int) dim;
|
|
to += dim;
|
|
}
|
|
|
|
/* copy knots */
|
|
to = result->knots + k + 1;
|
|
|
|
for( i = 0; i < n; i++ ) /* d) */
|
|
{
|
|
*to = deBoorNet->u;
|
|
to++;
|
|
}
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_evaluate( const tsBSpline* bspline, const tsReal u,
|
|
tsDeBoorNet* deBoorNet, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t deg = bspline->deg;
|
|
const size_t order = bspline->order;
|
|
const size_t dim = bspline->dim;
|
|
const size_t sof_c = dim * sizeof(tsReal); /* The size of a single
|
|
* control points.*/
|
|
size_t k;
|
|
size_t s;
|
|
tsReal uk; /* The actual used u. */
|
|
size_t from; /* An offset used to copy values. */
|
|
size_t fst; /* The first affected control point, inclusive. */
|
|
size_t lst; /* The last affected control point, inclusive. */
|
|
size_t N; /* The number of affected control points. */
|
|
/* the following indices are used to create the DeBoor net. */
|
|
size_t lidx; /* The current left index. */
|
|
size_t ridx; /* The current right index. */
|
|
size_t tidx; /* The current to index. */
|
|
size_t r, i, d; /* Used in for loop. */
|
|
tsReal ui; /* The knot value at index i. */
|
|
tsReal a, a_hat; /* The weighting factors of the control points. */
|
|
|
|
/* Setup the net with its default values. */
|
|
ts_deboornet_default( deBoorNet );
|
|
|
|
/* 1. Find index k such that u is in between [u_k, u_k+1).
|
|
* 2. Setup already known values.
|
|
* 3. Decide by multiplicity of u how to calculate point P(u). */
|
|
|
|
/* 1. */
|
|
ts_internal_bspline_find_u( bspline, u, &k, &s, buf );
|
|
deBoorNet->k = k;
|
|
deBoorNet->s = s;
|
|
|
|
/* 2. */
|
|
uk = bspline->knots[k]; /* Ensures that with any */
|
|
deBoorNet->u = ts_fequals( u, uk ) ? uk : u; /* tsReal precision the
|
|
* knot vector stays valid. */
|
|
deBoorNet->h = deg < s ? 0 : deg - s; /* prevent underflow */
|
|
deBoorNet->dim = dim;
|
|
|
|
/* 3. (by 1. s <= order)
|
|
*
|
|
* 3a) Check for s = order.
|
|
* Take the two points k-s and k-s + 1. If one of
|
|
* them doesn't exist, take only the other.
|
|
* 3b) Use de boor algorithm to find point P(u). */
|
|
if( s == order )
|
|
{
|
|
/* only one of the two control points exists */
|
|
if( k == deg /* only the first control point */
|
|
|| k == bspline->n_knots - 1 ) /* only the last control point */
|
|
{
|
|
deBoorNet->points = (tsReal*) malloc( sof_c );
|
|
|
|
if( deBoorNet->points == NULL )
|
|
longjmp( buf, TS_MALLOC );
|
|
|
|
deBoorNet->result = deBoorNet->points;
|
|
deBoorNet->n_points = 1;
|
|
from = k == deg ? 0 : (k - s) * dim;
|
|
memcpy( deBoorNet->points, bspline->ctrlp + from, sof_c );
|
|
}
|
|
else
|
|
{
|
|
deBoorNet->points = (tsReal*) malloc( 2 * sof_c );
|
|
|
|
if( deBoorNet->points == NULL )
|
|
longjmp( buf, TS_MALLOC );
|
|
|
|
deBoorNet->result = deBoorNet->points + dim;
|
|
deBoorNet->n_points = 2;
|
|
from = (k - s) * dim;
|
|
memcpy( deBoorNet->points, bspline->ctrlp + from, 2 * sof_c );
|
|
}
|
|
}
|
|
else /* by 3a) s <= deg (order = deg+1) */
|
|
{
|
|
fst = k - deg; /* by 1. k >= deg */
|
|
lst = k - s; /* s <= deg <= k */
|
|
N = lst - fst + 1; /* lst <= fst implies N >= 1 */
|
|
|
|
deBoorNet->n_points = (size_t) (N * (N + 1) * 0.5f); /* always fits */
|
|
deBoorNet->points = (tsReal*) malloc( deBoorNet->n_points * sof_c );
|
|
|
|
if( deBoorNet->points == NULL )
|
|
longjmp( buf, TS_MALLOC );
|
|
|
|
deBoorNet->result = deBoorNet->points + (deBoorNet->n_points - 1) * dim;
|
|
|
|
/* copy initial values to output */
|
|
memcpy( deBoorNet->points, bspline->ctrlp + fst * dim, N * sof_c );
|
|
|
|
lidx = 0;
|
|
ridx = dim;
|
|
tidx = N * dim; /* N >= 1 implies tidx > 0 */
|
|
r = 1;
|
|
|
|
for( ; r <= deBoorNet->h; r++ )
|
|
{
|
|
i = fst + r;
|
|
|
|
for( ; i <= lst; i++ )
|
|
{
|
|
ui = bspline->knots[i];
|
|
a = (deBoorNet->u - ui) / (bspline->knots[i + deg - r + 1] - ui);
|
|
a_hat = 1.f - a;
|
|
|
|
for( d = 0; d < dim; d++ )
|
|
{
|
|
deBoorNet->points[tidx++] =
|
|
a_hat * deBoorNet->points[lidx++] +
|
|
a * deBoorNet->points[ridx++];
|
|
}
|
|
}
|
|
|
|
lidx += dim;
|
|
ridx += dim;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_split( const tsBSpline* bspline, const tsReal u,
|
|
tsBSpline* split, size_t* k, jmp_buf buf
|
|
)
|
|
{
|
|
tsDeBoorNet net;
|
|
tsError e;
|
|
jmp_buf b;
|
|
|
|
TRY( b, e )
|
|
ts_internal_bspline_evaluate( bspline, u, &net, b );
|
|
|
|
if( net.s == bspline->order )
|
|
{
|
|
ts_internal_bspline_copy( bspline, split, b );
|
|
*k = net.k;
|
|
}
|
|
else
|
|
{
|
|
ts_internal_bspline_insert_knot(
|
|
bspline, &net, net.h + 1, split, b );
|
|
*k = net.k + net.h + 1;
|
|
}
|
|
|
|
CATCH
|
|
* k = 0;
|
|
ETRY ts_deboornet_free(& net );
|
|
|
|
if( e < 0 )
|
|
longjmp( buf, e );
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_thomas_algorithm( const tsReal* points, const size_t n, const size_t dim,
|
|
tsReal* output, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t sof_f = sizeof(tsReal); /* The size of a tsReal. */
|
|
const size_t sof_c = dim * sof_f; /* The size of a single control point. */
|
|
size_t len_m; /* The length m. */
|
|
tsReal* m; /* The array of weights. */
|
|
size_t lst; /* The index of the last control point in \points. */
|
|
size_t i, d; /* Used in for loops. */
|
|
size_t j, k, l; /* Used as temporary indices. */
|
|
|
|
/* input validation */
|
|
if( dim == 0 )
|
|
longjmp( buf, TS_DIM_ZERO );
|
|
|
|
if( n == 0 )
|
|
longjmp( buf, TS_DEG_GE_NCTRLP );
|
|
|
|
if( n <= 2 )
|
|
{
|
|
memcpy( output, points, n * sof_c );
|
|
return;
|
|
}
|
|
|
|
/* In the following n >= 3 applies... */
|
|
len_m = n - 2; /* ... len_m >= 1 */
|
|
lst = (n - 1) * dim; /* ... lst >= 2*dim */
|
|
|
|
/* m_0 = 1/4, m_{k+1} = 1/(4-m_k), for k = 0,...,n-2 */
|
|
m = (tsReal*) malloc( len_m * sof_f );
|
|
|
|
if( m == NULL )
|
|
longjmp( buf, TS_MALLOC );
|
|
|
|
m[0] = 0.25f;
|
|
|
|
for( i = 1; i < len_m; i++ )
|
|
m[i] = 1.f / (4 - m[i - 1]);
|
|
|
|
/* forward sweep */
|
|
ts_arr_fill( output, n * dim, 0.f );
|
|
memcpy( output, points, sof_c );
|
|
memcpy( output + lst, points + lst, sof_c );
|
|
|
|
for( d = 0; d < dim; d++ )
|
|
{
|
|
k = dim + d;
|
|
output[k] = 6 * points[k];
|
|
output[k] -= points[d];
|
|
}
|
|
|
|
for( i = 2; i <= n - 2; i++ )
|
|
{
|
|
for( d = 0; d < dim; d++ )
|
|
{
|
|
j = (i - 1) * dim + d;
|
|
k = i * dim + d;
|
|
l = (i + 1) * dim + d;
|
|
output[k] = 6 * points[k];
|
|
output[k] -= output[l];
|
|
output[k] -= m[i - 2] * output[j]; /* i >= 2 */
|
|
}
|
|
}
|
|
|
|
/* back substitution */
|
|
if( n > 3 )
|
|
ts_arr_fill( output + lst, dim, 0.f );
|
|
|
|
for( i = n - 2; i >= 1; i-- )
|
|
{
|
|
for( d = 0; d < dim; d++ )
|
|
{
|
|
k = i * dim + d;
|
|
l = (i + 1) * dim + d;
|
|
/* The following line is the reason why it's important to not fill
|
|
* output with 0 if n = 3. On the other hand, if n > 3 subtracting
|
|
* 0 is exactly what we want. */
|
|
output[k] -= output[l];
|
|
output[k] *= m[i - 1]; /* i >= 1 */
|
|
}
|
|
}
|
|
|
|
if( n > 3 )
|
|
memcpy( output + lst, points + lst, sof_c );
|
|
|
|
/* we are done */
|
|
free( m );
|
|
}
|
|
|
|
|
|
void ts_internal_relaxed_uniform_cubic_bspline( const tsReal* points,
|
|
const size_t n,
|
|
const size_t dim,
|
|
tsBSpline* bspline,
|
|
jmp_buf buf
|
|
)
|
|
{
|
|
const size_t order = 4; /* The order of the spline to interpolate. */
|
|
const tsReal as = 1.f / 6.f; /* The value 'a sixth'. */
|
|
const tsReal at = 1.f / 3.f; /* The value 'a third'. */
|
|
const tsReal tt = 2.f / 3.f; /* The value 'two third'. */
|
|
size_t sof_c; /* The size of a single control point. */
|
|
const tsReal* b = points; /* The array of the b values. */
|
|
tsReal* s; /* The array of the s values. */
|
|
size_t i, d; /* Used in for loops */
|
|
size_t j, k, l; /* Uses as temporary indices. */
|
|
tsError e_;
|
|
jmp_buf b_;
|
|
|
|
/* input validation */
|
|
if( dim == 0 )
|
|
longjmp( buf, TS_DIM_ZERO );
|
|
|
|
if( n <= 1 )
|
|
longjmp( buf, TS_DEG_GE_NCTRLP );
|
|
|
|
/* in the following n >= 2 applies */
|
|
|
|
sof_c = dim * sizeof(tsReal); /* dim > 0 implies sof_c > 0 */
|
|
|
|
/* n >= 2 implies n-1 >= 1 implies (n-1)*4 >= 4 */
|
|
ts_internal_bspline_new( (n - 1) * 4, dim, order - 1, TS_BEZIERS, bspline, buf );
|
|
|
|
TRY( b_, e_ )
|
|
s = (tsReal*) malloc( n * sof_c );
|
|
|
|
if( s == NULL )
|
|
longjmp( b_, TS_MALLOC );
|
|
|
|
CATCH ts_bspline_free( bspline );
|
|
|
|
longjmp( buf, e_ );
|
|
ETRY
|
|
/* set s_0 to b_0 and s_n = b_n */
|
|
memcpy( s, b, sof_c );
|
|
|
|
memcpy( s + (n - 1) * dim, b + (n - 1) * dim, sof_c );
|
|
|
|
/* set s_i = 1/6*b_i + 2/3*b_{i-1} + 1/6*b_{i+1}*/
|
|
for( i = 1; i < n - 1; i++ )
|
|
{
|
|
for( d = 0; d < dim; d++ )
|
|
{
|
|
j = (i - 1) * dim + d;
|
|
k = i * dim + d;
|
|
l = (i + 1) * dim + d;
|
|
s[k] = as * b[j];
|
|
s[k] += tt * b[k];
|
|
s[k] += as * b[l];
|
|
}
|
|
}
|
|
|
|
/* create beziers from b and s */
|
|
for( i = 0; i < n - 1; i++ )
|
|
{
|
|
for( d = 0; d < dim; d++ )
|
|
{
|
|
j = i * dim + d;
|
|
k = i * 4 * dim + d;
|
|
l = (i + 1) * dim + d;
|
|
bspline->ctrlp[k] = s[j];
|
|
bspline->ctrlp[k + dim] = tt * b[j] + at * b[l];
|
|
bspline->ctrlp[k + 2 * dim] = at * b[j] + tt * b[l];
|
|
bspline->ctrlp[k + 3 * dim] = s[l];
|
|
}
|
|
}
|
|
|
|
free( s );
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_interpolate_cubic( const tsReal* points, const size_t n, const size_t dim,
|
|
tsBSpline* bspline, jmp_buf buf
|
|
)
|
|
{
|
|
tsError e;
|
|
jmp_buf b;
|
|
tsReal* thomas = (tsReal*) malloc( n * dim * sizeof(tsReal) );
|
|
|
|
if( thomas == NULL )
|
|
longjmp( buf, TS_MALLOC );
|
|
|
|
TRY( b, e )
|
|
ts_internal_bspline_thomas_algorithm( points, n, dim, thomas, b );
|
|
ts_internal_relaxed_uniform_cubic_bspline( thomas, n, dim, bspline, b );
|
|
ETRY free( thomas );
|
|
|
|
if( e < 0 )
|
|
longjmp( buf, e );
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_derive( const tsBSpline* original,
|
|
tsBSpline* derivative, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t sof_f = sizeof(tsReal);
|
|
const size_t dim = original->dim;
|
|
const size_t deg = original->deg;
|
|
const size_t nc = original->n_ctrlp;
|
|
const size_t nk = original->n_knots;
|
|
tsReal* from_ctrlp = original->ctrlp;
|
|
tsReal* from_knots = original->knots;
|
|
tsReal* to_ctrlp = NULL;
|
|
tsReal* to_knots = NULL;
|
|
size_t i, j, k;
|
|
|
|
if( deg < 1 || nc < 2 )
|
|
longjmp( buf, TS_UNDERIVABLE );
|
|
|
|
if( original != derivative )
|
|
{
|
|
ts_internal_bspline_new( nc - 1, dim, deg - 1, TS_NONE, derivative, buf );
|
|
to_ctrlp = derivative->ctrlp;
|
|
to_knots = derivative->knots;
|
|
}
|
|
else
|
|
{
|
|
to_ctrlp = (tsReal*) malloc( ( (nc - 1) * dim + (nk - 2) ) * sof_f );
|
|
|
|
if( to_ctrlp == NULL )
|
|
longjmp( buf, TS_MALLOC );
|
|
|
|
to_knots = to_ctrlp + (nc - 1) * dim;
|
|
}
|
|
|
|
for( i = 0; i < nc - 1; i++ )
|
|
{
|
|
for( j = 0; j < dim; j++ )
|
|
{
|
|
if( ts_fequals( from_knots[i + deg + 1], from_knots[i + 1] ) )
|
|
{
|
|
free( to_ctrlp );
|
|
longjmp( buf, TS_UNDERIVABLE );
|
|
}
|
|
else
|
|
{
|
|
k = i * dim + j;
|
|
to_ctrlp[k] = from_ctrlp[(i + 1) * dim + j] - from_ctrlp[k];
|
|
to_ctrlp[k] *= deg;
|
|
to_ctrlp[k] /= from_knots[i + deg + 1] - from_knots[i + 1];
|
|
}
|
|
}
|
|
}
|
|
|
|
memcpy( to_knots, from_knots + 1, (nk - 2) * sof_f );
|
|
|
|
if( original == derivative )
|
|
{
|
|
/* free old memory */
|
|
free( from_ctrlp );
|
|
/* assign new values */
|
|
derivative->deg = deg - 1;
|
|
derivative->order = deg;
|
|
derivative->n_ctrlp = nc - 1;
|
|
derivative->n_knots = nk - 2;
|
|
derivative->ctrlp = to_ctrlp;
|
|
derivative->knots = to_knots;
|
|
}
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_buckle( const tsBSpline* bspline, const tsReal b,
|
|
tsBSpline* buckled, jmp_buf buf
|
|
)
|
|
{
|
|
const tsReal b_hat = 1.f - b; /* The straightening factor. */
|
|
const size_t dim = bspline->dim;
|
|
const size_t N = bspline->n_ctrlp;
|
|
const tsReal* p0 = bspline->ctrlp; /* Pointer to first ctrlp. */
|
|
const tsReal* pn_1 = p0 + (N - 1) * dim; /* Pointer to the last ctrlp. */
|
|
size_t i, d; /* Used in for loops. */
|
|
|
|
ts_internal_bspline_copy( bspline, buckled, buf );
|
|
|
|
for( i = 0; i < N; i++ )
|
|
{
|
|
for( d = 0; d < dim; d++ )
|
|
{
|
|
buckled->ctrlp[i * dim + d] =
|
|
b * buckled->ctrlp[i * dim + d] +
|
|
b_hat * ( p0[d] + ( (tsReal) i / (N - 1) ) * (pn_1[d] - p0[d]) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_to_beziers( const tsBSpline* bspline,
|
|
tsBSpline* beziers, jmp_buf buf
|
|
)
|
|
{
|
|
tsError e;
|
|
jmp_buf b;
|
|
const size_t deg = bspline->deg;
|
|
const size_t order = bspline->order;
|
|
tsBSpline tmp;
|
|
int resize; /* The number of control points to add/remove. */
|
|
size_t k; /* The index of the splitted knot value. */
|
|
tsReal u_min; /* The minimum of the knot values. */
|
|
tsReal u_max; /* The maximum of the knot values. */
|
|
|
|
ts_internal_bspline_copy( bspline, &tmp, buf );
|
|
|
|
TRY( b, e )
|
|
/* fix first control point if necessary */
|
|
u_min = tmp.knots[deg];
|
|
|
|
if( !ts_fequals( tmp.knots[0], u_min ) )
|
|
{
|
|
ts_internal_bspline_split( &tmp, u_min, &tmp, &k, b );
|
|
resize = (int) ( -1 * deg + (deg * 2 - k) );
|
|
ts_internal_bspline_resize( &tmp, resize, 0, &tmp, b );
|
|
}
|
|
|
|
/* fix last control point if necessary */
|
|
u_max = tmp.knots[tmp.n_knots - order];
|
|
|
|
if( !ts_fequals( tmp.knots[tmp.n_knots - 1], u_max ) )
|
|
{
|
|
ts_internal_bspline_split( &tmp, u_max, &tmp, &k, b );
|
|
resize = (int) ( -1 * deg + ( k - (tmp.n_knots - order) ) );
|
|
ts_internal_bspline_resize( &tmp, resize, 1, &tmp, b );
|
|
}
|
|
|
|
k = order;
|
|
|
|
while( k < tmp.n_knots - order )
|
|
{
|
|
ts_internal_bspline_split( &tmp, tmp.knots[k], &tmp, &k, b );
|
|
k++;
|
|
}
|
|
|
|
if( bspline == beziers )
|
|
ts_bspline_free( beziers );
|
|
|
|
ts_bspline_move( &tmp, beziers );
|
|
ETRY ts_bspline_free(& tmp );
|
|
|
|
if( e < 0 )
|
|
longjmp( buf, e );
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_set_ctrlp( const tsBSpline* bspline, const tsReal* ctrlp,
|
|
tsBSpline* result, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t s = bspline->n_ctrlp * bspline->dim * sizeof(tsReal);
|
|
|
|
ts_internal_bspline_copy( bspline, result, buf );
|
|
memmove( result->ctrlp, ctrlp, s );
|
|
}
|
|
|
|
|
|
void ts_internal_bspline_set_knots( const tsBSpline* bspline, const tsReal* knots,
|
|
tsBSpline* result, jmp_buf buf
|
|
)
|
|
{
|
|
const size_t s = bspline->n_knots * sizeof(tsReal);
|
|
|
|
ts_internal_bspline_copy( bspline, result, buf );
|
|
memmove( result->knots, knots, s );
|
|
}
|
|
|
|
|
|
/********************************************************
|
|
* *
|
|
* Interface implementation *
|
|
* *
|
|
********************************************************/
|
|
void ts_deboornet_default( tsDeBoorNet* deBoorNet )
|
|
{
|
|
deBoorNet->u = 0.f;
|
|
deBoorNet->k = 0;
|
|
deBoorNet->s = 0;
|
|
deBoorNet->h = 0;
|
|
deBoorNet->dim = 0;
|
|
deBoorNet->n_points = 0;
|
|
deBoorNet->points = NULL;
|
|
deBoorNet->result = NULL;
|
|
}
|
|
|
|
|
|
void ts_deboornet_move( tsDeBoorNet* from, tsDeBoorNet* to )
|
|
{
|
|
if( from == to )
|
|
return;
|
|
|
|
to->u = from->u;
|
|
to->k = from->k;
|
|
to->s = from->s;
|
|
to->h = from->h;
|
|
to->dim = from->dim;
|
|
to->n_points = from->n_points;
|
|
to->points = from->points;
|
|
to->result = from->result;
|
|
ts_deboornet_default( from );
|
|
}
|
|
|
|
|
|
void ts_deboornet_free( tsDeBoorNet* deBoorNet )
|
|
{
|
|
if( deBoorNet->points != NULL )
|
|
free( deBoorNet->points ); /* automatically frees the field result */
|
|
|
|
ts_deboornet_default( deBoorNet );
|
|
}
|
|
|
|
|
|
void ts_bspline_default( tsBSpline* bspline )
|
|
{
|
|
bspline->deg = 0;
|
|
bspline->order = 0;
|
|
bspline->dim = 0;
|
|
bspline->n_ctrlp = 0;
|
|
bspline->n_knots = 0;
|
|
bspline->ctrlp = NULL;
|
|
bspline->knots = NULL;
|
|
}
|
|
|
|
|
|
void ts_bspline_free( tsBSpline* bspline )
|
|
{
|
|
if( bspline->ctrlp != NULL )
|
|
free( bspline->ctrlp );
|
|
|
|
ts_bspline_default( bspline );
|
|
}
|
|
|
|
|
|
void ts_bspline_move( tsBSpline* from, tsBSpline* to )
|
|
{
|
|
if( from == to )
|
|
return;
|
|
|
|
to->deg = from->deg;
|
|
to->order = from->order;
|
|
to->dim = from->dim;
|
|
to->n_ctrlp = from->n_ctrlp;
|
|
to->n_knots = from->n_knots;
|
|
to->ctrlp = from->ctrlp;
|
|
to->knots = from->knots;
|
|
ts_bspline_default( from );
|
|
}
|
|
|
|
|
|
tsError ts_bspline_new( size_t n_ctrlp, size_t dim, size_t deg, tsBSplineType type,
|
|
tsBSpline* bspline
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_new( n_ctrlp, dim, deg, type, bspline, buf );
|
|
CATCH ts_bspline_default( bspline );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_interpolate_cubic( const tsReal* points, size_t n, size_t dim,
|
|
tsBSpline* bspline
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_interpolate_cubic( points, n, dim, bspline, buf );
|
|
CATCH ts_bspline_default( bspline );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_derive( const tsBSpline* original,
|
|
tsBSpline* derivative
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_derive( original, derivative, buf );
|
|
CATCH
|
|
|
|
if( original != derivative )
|
|
ts_bspline_default( derivative );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_deboornet_copy( const tsDeBoorNet* original,
|
|
tsDeBoorNet* copy
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_deboornet_copy( original, copy, buf );
|
|
CATCH
|
|
|
|
if( original != copy )
|
|
ts_deboornet_default( copy );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_copy( const tsBSpline* original,
|
|
tsBSpline* copy
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_copy( original, copy, buf );
|
|
CATCH
|
|
|
|
if( original != copy )
|
|
ts_bspline_default( copy );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_set_ctrlp( const tsBSpline* bspline, const tsReal* ctrlp,
|
|
tsBSpline* result
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_set_ctrlp( bspline, ctrlp, result, buf );
|
|
CATCH
|
|
|
|
if( bspline != result )
|
|
ts_bspline_default( result );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_set_knots( const tsBSpline* bspline, const tsReal* knots,
|
|
tsBSpline* result
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_set_knots( bspline, knots, result, buf );
|
|
CATCH
|
|
|
|
if( bspline != result )
|
|
ts_bspline_default( result );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_fill_knots( const tsBSpline* original,
|
|
tsBSplineType type,
|
|
tsReal min,
|
|
tsReal max,
|
|
tsBSpline* result
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_fill_knots( original, type, min, max, result, buf );
|
|
CATCH
|
|
|
|
if( original != result )
|
|
ts_bspline_default( result );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_evaluate( const tsBSpline* bspline, tsReal u,
|
|
tsDeBoorNet* deBoorNet
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_evaluate( bspline, u, deBoorNet, buf );
|
|
CATCH ts_deboornet_default( deBoorNet );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_insert_knot( const tsBSpline* bspline, tsReal u, size_t n,
|
|
tsBSpline* result, size_t* k
|
|
)
|
|
{
|
|
tsDeBoorNet net;
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_evaluate( bspline, u, &net, buf );
|
|
ts_internal_bspline_insert_knot( bspline, &net, n, result, buf );
|
|
*k = net.k + n;
|
|
CATCH
|
|
|
|
if( bspline != result )
|
|
ts_bspline_default( result );
|
|
|
|
*k = 0;
|
|
ETRY ts_deboornet_free(& net );
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_resize( const tsBSpline* bspline, int n, int back,
|
|
tsBSpline* resized
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_resize( bspline, n, back, resized, buf );
|
|
CATCH
|
|
|
|
if( bspline != resized )
|
|
ts_bspline_default( resized );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_split( const tsBSpline* bspline, tsReal u,
|
|
tsBSpline* split, size_t* k
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_split( bspline, u, split, k, buf );
|
|
CATCH
|
|
|
|
if( bspline != split )
|
|
ts_bspline_default( split );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_buckle( const tsBSpline* bspline, tsReal b,
|
|
tsBSpline* buckled
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_buckle( bspline, b, buckled, buf );
|
|
CATCH
|
|
|
|
if( bspline != buckled )
|
|
ts_bspline_default( buckled );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
tsError ts_bspline_to_beziers( const tsBSpline* bspline,
|
|
tsBSpline* beziers
|
|
)
|
|
{
|
|
tsError err;
|
|
jmp_buf buf;
|
|
|
|
TRY( buf, err )
|
|
ts_internal_bspline_to_beziers( bspline, beziers, buf );
|
|
CATCH
|
|
|
|
if( bspline != beziers )
|
|
ts_bspline_default( beziers );
|
|
|
|
ETRY
|
|
return err;
|
|
}
|
|
|
|
|
|
int ts_fequals( tsReal x, tsReal y )
|
|
{
|
|
if( fabs( x - y ) <= FLT_MAX_ABS_ERROR )
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
const tsReal r = (tsReal) fabs( x ) > (tsReal) fabs( y ) ?
|
|
(tsReal) fabs( (x - y) / x ) : (tsReal) fabs( (x - y) / y );
|
|
return r <= FLT_MAX_REL_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
const char* ts_enum_str( tsError err )
|
|
{
|
|
if( err == TS_MALLOC )
|
|
return "malloc failed";
|
|
else if( err == TS_DIM_ZERO )
|
|
return "dim == 0";
|
|
else if( err == TS_DEG_GE_NCTRLP )
|
|
return "deg >= #ctrlp";
|
|
else if( err == TS_U_UNDEFINED )
|
|
return "spline is undefined at given u";
|
|
else if( err == TS_MULTIPLICITY )
|
|
return "s > order";
|
|
else if( err == TS_KNOTS_DECR )
|
|
return "decreasing knot vector";
|
|
else if( err == TS_NUM_KNOTS )
|
|
return "unexpected number of knots";
|
|
else if( err == TS_UNDERIVABLE )
|
|
return "spline is not derivable";
|
|
|
|
return "unknown error";
|
|
}
|
|
|
|
|
|
tsError ts_str_enum( const char* str )
|
|
{
|
|
if( !strcmp( str, ts_enum_str( TS_MALLOC ) ) )
|
|
return TS_MALLOC;
|
|
else if( !strcmp( str, ts_enum_str( TS_DIM_ZERO ) ) )
|
|
return TS_DIM_ZERO;
|
|
else if( !strcmp( str, ts_enum_str( TS_DEG_GE_NCTRLP ) ) )
|
|
return TS_DEG_GE_NCTRLP;
|
|
else if( !strcmp( str, ts_enum_str( TS_U_UNDEFINED ) ) )
|
|
return TS_U_UNDEFINED;
|
|
else if( !strcmp( str, ts_enum_str( TS_MULTIPLICITY ) ) )
|
|
return TS_MULTIPLICITY;
|
|
else if( !strcmp( str, ts_enum_str( TS_KNOTS_DECR ) ) )
|
|
return TS_KNOTS_DECR;
|
|
else if( !strcmp( str, ts_enum_str( TS_NUM_KNOTS ) ) )
|
|
return TS_NUM_KNOTS;
|
|
else if( !strcmp( str, ts_enum_str( TS_UNDERIVABLE ) ) )
|
|
return TS_UNDERIVABLE;
|
|
|
|
return TS_SUCCESS;
|
|
}
|
|
|
|
|
|
void ts_arr_fill( tsReal* arr, size_t num, tsReal val )
|
|
{
|
|
size_t i;
|
|
|
|
for( i = 0; i < num; i++ )
|
|
arr[i] = val;
|
|
}
|
|
|
|
|
|
tsReal ts_ctrlp_dist2( const tsReal* x, const tsReal* y, size_t dim
|
|
)
|
|
{
|
|
tsReal sum = 0;
|
|
size_t i;
|
|
|
|
for( i = 0; i < dim; i++ )
|
|
sum += (x[i] - y[i]) * (x[i] - y[i]);
|
|
|
|
return (tsReal) sqrt( sum );
|
|
}
|