/** @file tinyspline.h */ /* * 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. */ #ifndef TINYSPLINE_H #define TINYSPLINE_H #include #ifdef __cplusplus extern "C" { #endif /****************************************************************************** * * * System Dependent Configuration * * * * The following configuration values must be adjusted to your system. Some of * * them may be configured using preprocessor definitions. The default values * * should be fine for most modern hardware, such as x86, x86_64, and arm. * * * ******************************************************************************/ #ifdef TINYSPLINE_FLOAT_PRECISION typedef float tsReal; #else typedef double tsReal; #endif #define FLT_MAX_ABS_ERROR 1e-5 #define FLT_MAX_REL_ERROR 1e-8 /****************************************************************************** * * * Data Types * * * * The following section defines all data types available in TinySpline. * * * ******************************************************************************/ /** * Contains all error codes used by TinySpline. The following code snippet * shows how to handle errors: * * tsError err = ... // any function call here * if (err < 0) { // or use err != TS_SUCCESS * printf("we got an error!"); * * // you may want to reuse error codes * return err; * } */ typedef enum { /* No error. */ TS_SUCCESS = 0, /* Unable to allocate memory (using malloc/realloc). */ TS_MALLOC = -1, /* The dimension of the control points are 0. */ TS_DIM_ZERO = -2, /* Degree of spline (deg) >= number of control points (n_ctrlp). */ TS_DEG_GE_NCTRLP = -3, /* Spline is not defined at knot value u. */ TS_U_UNDEFINED = -4, /* Multiplicity of a knot (s) > order of spline. */ TS_MULTIPLICITY = -5, /* Decreasing knot vector. */ TS_KNOTS_DECR = -6, /* Unexpected number of knots. */ TS_NUM_KNOTS = -7, /* Spline is not derivable */ TS_UNDERIVABLE = -8 } tsError; /** * Describes how the knot vector of a spline is organized. If you don't know * what an opened or clamped spline is, have a look at: * * www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve.html */ typedef enum { /* Not available/Undefined. */ TS_NONE = 0, /* Uniformly spaced knot vector. */ TS_OPENED = 1, /* Uniformly spaced knot vector with clamped end knots. */ TS_CLAMPED = 2, /* Uniformly spaced knot vector with s(u) = order of spline. */ TS_BEZIERS = 3 } tsBSplineType; /** * Represents a B-Spline which may also be used for NURBS, Bezier curves, * lines, and points. NURBS are represented using homogeneous coordinates where * the last component of a control point is its weight. Bezier curves are * B-Splines with 'n_ctrlp == order' and clamped knot vector making the curve * passing through the first and last control point. If a Bezier curve consists * of two control points only, we call them a line. Points, ultimately, are * just very short lines having only a single control point. Consequently, the * degree of a point is zero. * * Two dimensional control points are organized as follows: * * [x_0, y_0, x_1, y_1, ..., x_n-1, y_n-1] * * Tree dimensional control points are organized as follows: * * [x_0, y_0, z_0, x_1, y_1, z_1, ..., x_n-1, y_n-1, z_n-1] * * ... and so on. NURBS are represented using homogeneous coordinates. For * instance, let's say we have a NURBS in 2D consisting of 11 control points * where 'w_i' is the weight of the i'th control point. Then the corresponding * control points are organized as follows: * * [x_0, y_0, w_0, x_1, y_1, w_1, ..., x_10, y_10, w_10] * * Note: The fields 'ctrlp' and 'knots' share the same array (similar to the * approach used in 'tsDeBoorNet'). That is, the first elements of this * array contain the control points of a spline whereas the last elements * contain its knots. Accordingly, you should never free 'knots' * explicitly. Using 'ts_bspline_free()' to free dynamically allocated * memory is to be preferred anyway. If 'ctrlp' and 'knots' do not share * the same array, or at least a consistent block of data, functions * provided by TinySpline my fail because values are copied block wise. */ typedef struct { /* Degree of B-Spline basis function. */ size_t deg; /* A convenience field for deg+1. */ size_t order; /* Dimension of a control points. */ size_t dim; /* Number of control points. */ size_t n_ctrlp; /* Number of knots (n_ctrlp + deg + 1). */ size_t n_knots; /* Control points of a spline. */ tsReal* ctrlp; /* Knot vector of a spline (ascending order). */ tsReal* knots; } tsBSpline; /** * Represents the output of De Boor's algorithm. De Boor's algorithm is used to * evaluate a spline at given knot value 'u' by iteratively computing a net of * intermediate values until the result is available: * * https://en.wikipedia.org/wiki/De_Boor%27s_algorithm * https://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/de-Boor.html * * All points of the net are stored in 'points'. The resulting point of an * evaluation is the last point in 'points' and, for the sake of convenience, * may be accessed using 'result': * * tsDeBoorNet net = ... // evaluate an arbitrary spline and store * // resulting net of points in 'net' * * net.result ... // use 'result' to access resulting point * * Note: You should never free 'result' explicitly as it is just a convenient * accessor for the last point in 'points'. Using 'ts_deboornet_free()' * to free dynamically allocated memory is to be preferred anyway. * * Two dimensional points are organized as follows: * * [x_0, y_0, x_1, y_1, ..., x_n-1, y_n-1] * * Tree dimensional points are organized as follows: * * [x_0, y_0, z_0, x_1, y_1, z_1, ..., x_n-1, y_n-1, z_n-1] * * ... and so on. * * There is a special case in which the evaluation of a knot value 'u' returns * two instead of one result. It occurs when the multiplicity of 'u' ( s(u) ) * is equals to a spline's order indicating that the spline is discontinuous at * 'u'. This is common practice for B-Splines (or NURBS) consisting of * connected Bezier curves where the endpoint of curve 'c_i' is equals to the * start point of curve 'c_i+1'. The end point of 'c_i' and the start point of * 'c_i+1' may still be completely different though, yielding to a spline * having a (real and visible) gap at 'u'. Consequently, De Boor's algorithm * must return two results if 's(u) == order' in order to give you access to * the desired points. In such case, 'points' stores only the two resulting * points (there is no net to create) and 'result' points to the *first* point * in 'points' ('points' and 'result' store the same pointer). Since having * (real) gaps in splines is unusual, both points in 'points' are generally * equals making it easy to handle this special case by accessing 'result' as * already shown above for regular cases: * * tsDeBoorNet net = ... // evaluate a spline which is discontinuous at * // at given knot value yielding to a net with * // two results * * net.result ... // use 'result' to access resulting point * * However, you can use both points if necessary: * * tsDeBoorNet net = ... // evaluate a spline which is discontinuous at * // at given knot value yielding to a net with * // two results * * net.result[0] ... // 'result[0]' stores the first component of * // the first point * * net.result[net.dim] // 'result[net.dim]' stores the first component * // of the second point * * As if this wasn't complicated enough, there is an exception for our special * case yielding to exactly one result (just like the regular case) even if * 's(u) == order'. It occurs when 'u' is the lower or upper bound of a * spline's domain. For instance, if 'b' is a spline with domain [0, 1] and is * evaluated at 'u = 0' or 'u = 1' then 'result' is *always* a single point * regardless of the multiplicity of 'u'. Hence, handling this exception is * straightforward: * * tsDeBoorNet net = ... // evaluate a spline at lower or upper bound of * // its domain, for instance, 0 or 1 * * net.result ... // use 'result' to access resulting point * * In summary, we have three different types of evaluation. 1) The regular case * returning all points of the net we used to calculate the resulting point. 2) * The special case returning exactly two points which is required for splines * having (real) gaps. 3) The exception of 2) returning exactly one point even * if 's(u) == order'. All in all this looks quite complex (and actually it is) * but for most applications you don't need to bother with them. Just use * 'result' to access your evaluation point. */ typedef struct { /* The evaluated knot value. */ tsReal u; /* The index [u_k, u_k+1) */ size_t k; /* Multiplicity of u_k. */ size_t s; /* How many times u must be inserted to get the resulting point. */ size_t h; /* Dimension of a control point. */ size_t dim; /* Number of points in 'points'. */ size_t n_points; /* Points of the net used to evaluate u_k. */ tsReal* points; /* A convenient pointer to the result in 'points'. */ tsReal* result; } tsDeBoorNet; /****************************************************************************** * * * Constructors, Destructors, Copy, and Move Functions * * * * The following section contains functions to create and delete instances of * * the data types listed above. Additionally, each data type has a copy and * * move function. * * * ******************************************************************************/ /** * The default constructor of tsBSpline. * * All values of \p \_spline\_ are set to 0/NULL. * * @param \_spline\_ * The spline whose values are set 0/NULL. */ void ts_bspline_default( tsBSpline* _spline_ ); /** * A convenient constructor for tsBSpline. * * On error, all values of \p \_spline\_ are set to 0/NULL. * * @param n_ctrlp * The number of control points of \p \_spline\_. * @param dim * The dimension of each control point in \p \_spline\_. * @param deg * The degree of \p \_spline\_. * @param type * How to setup the knot vector of \p \_spline\_. * @param \_spline\_ * The output parameter storing the result of this function. * @return TS_SUCCESS * On success. * @return TS_DIM_ZERO * If \p deg == 0. * @return TS_DEG_GE_NCTRLP * If \p deg >= \p n_ctrlp. * @return TS_NUM_KNOTS * If \p type == ::TS_BEZIERS and (\p n_ctrlp % \p deg + 1) != 0. * @return TS_MALLOC * If allocating memory failed. */ tsError ts_bspline_new( size_t n_ctrlp, size_t dim, size_t deg, tsBSplineType type, tsBSpline* _spline_ ); /** * The copy constructor of tsBSpline. * * Creates a deep copy of \p original and stores the result in \p \_copy\_. * * On error, all values of \p \_copy\_ are set to 0/NULL. Does nothing, if * \p original == \p \_copy\_. * * @param original * The spline to deep copy. * @param \_copy\_ * The output parameter storing the copied values of \p original. * @return TS_SUCCESS * On success. * @return TS_MALLOC * If allocating memory failed. */ tsError ts_bspline_copy( const tsBSpline* original, tsBSpline* _copy_ ); /** * The move constructor of tsBSpline. * * Moves all values from \p from to \p \_to\_ and calls ::ts_bspline_default * on \p from afterwards. Does nothing, if \p from == \p \_to\_. * * @param from * The spline whose values are moved to \p \_to\_. * @param \_to\_ * The output parameter storing the moved values of \p from. */ void ts_bspline_move( tsBSpline* from, tsBSpline* _to_ ); /** * The destructor of tsBSpline. * * Frees all dynamically allocated memory in \p \_spline\_ and calls * ::ts_bspline_default afterwards. * * @param \_spline\_ * The spline to free. */ void ts_bspline_free( tsBSpline* _spline_ ); /** * The default constructor of tsDeBoorNet. * * All values of \p \_deBoorNet\_ are set to 0/NULL. * * @param \_deBoorNet\_ * The net whose values are set 0/NULL. */ void ts_deboornet_default( tsDeBoorNet* _deBoorNet_ ); /** * The copy constructor of tsDeBoorNet. * * Creates a deep copy of \p original and stores the result in \p \_copy\_. * * On error, all values of \p _copy_ are set to 0/NULL. Does nothing, if * \p original == \p \_copy\_. * * @param original * The net to deep copy. * @param \_copy\_ * The output parameter storing the copied values of \p original. * @return TS_SUCCESS * On success. * @return TS_MALLOC * If allocating memory failed. */ tsError ts_deboornet_copy( const tsDeBoorNet* original, tsDeBoorNet* _copy_ ); /** * The move constructor of tsDeBoorNet. * * Moves all values from \p from to \p \_to\_ and calls ::ts_deboornet_default * on \p from afterwards. Does nothing, if \p from == \p \_to\_. * * @param from * The net whose values are moved to \p \_to\_. * @param \_to\_ * The output parameter storing the moved values of \p from. */ void ts_deboornet_move( tsDeBoorNet* from, tsDeBoorNet* _to_ ); /** * The destructor of tsDeBoorNet. * * Frees all dynamically allocated memory in \p \_deBoorNet\_ and calls * ::ts_deboornet_default afterwards. * * @param \_deBoorNet\_ * The net to free. */ void ts_deboornet_free( tsDeBoorNet* _deBoorNet_ ); /****************************************************************************** * * * Interpolation and Approximation Functions * * * * The following section contains functions to interpolate and approximate * * arbitrary splines. * * * ******************************************************************************/ /** * Performs a cubic spline interpolation using the thomas algorithm, see: * * https://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm * http://www.math.ucla.edu/~baker/149.1.02w/handouts/dd_splines.pdf * http://www.bakoma-tex.com/doc/generic/pst-bspline/pst-bspline-doc.pdf * * The resulting spline is a sequence of bezier curves connecting each point * in \p points. Each bezier curve _b_ is of degree 3 with \p dim being the * dimension of the each control point in _b_. The total number of control * points is (\p n - 1) * 4. * * On error, all values of \p \_spline\_ are set to 0/NULL. * * Note: \p n is the number of points in \p points and not the length of * \p points. For instance, the follwing point vector yields to \p n = 4 and * \p dim = 2: * * [x0, y0, x1, y1, x2, y2, x3, y3] * * @param points * The points to interpolate. * @param n * The number of points in \p points. * @param dim * The dimension of each control point in \p \_spline\_. * @param \_spline\_ * The output parameter storing the result of this function. * @return TS_SUCCESS * On success. * @return TS_DIM_ZERO * If \p dim == 0. * @return TS_DEG_GE_NCTRLP * If \p n < 2. * @return TS_MALLOC * If allocating memory failed. */ tsError ts_bspline_interpolate_cubic( const tsReal* points, size_t n, size_t dim, tsBSpline* _spline_ ); /****************************************************************************** * * * Query Functions * * * * The following section contains functions to query splines. * * * ******************************************************************************/ /** * Evaluates \p spline at knot value \p u and stores the result in * \p \_deBoorNet\_. * * On error, all values of \p \_deBoorNet\_ are set to 0/NULL. * * @param spline * The spline to evaluate. * @param u * The knot value to evaluate. * @param \_deBoorNet\_ * The output parameter storing the evaluation result. * @return TS_SUCCESS * On success. * @return TS_MULTIPLICITY * If multiplicity s(\p u) > order of \p spline. * @return TS_U_UNDEFINED * If \p spline is not defined at knot value \p u. * @return TS_MALLOC * If allocating memory failed. */ tsError ts_bspline_evaluate( const tsBSpline* spline, tsReal u, tsDeBoorNet* _deBoorNet_ ); /****************************************************************************** * * * Transformation functions * * * * TinySpline is a library focusing on transformations. That is, most * * functions are used to transform splines by modifying their state, e.g., * * their number of control points, their degree, and so on. Accordingly, each * * transformation functions specifies an input and output parameter (along * * with the other parameters required to calculate the actual transformation). * * By passing a different pointer to the output parameter, the transformation * * result is calculated and stored without changing the state of the input. * * This is in particular useful when dealing with errors as the original state * * will never be modified. For instance, let's have a look at the following * * code snippet: * * * * tsBSpline in = ... // an arbitrary spline * * tsBSpline out; // result of transformation * * * * // Subdivide 'in' into sequence of bezier curves and store the result * * // in 'out'. Does not change 'in' in any way. * * tsError err = ts_bspline_to_beziers(&in, &out); * * if (err != TS_SUCCESS) { * * // fortunately, 'in' has not been changed * * } * * * * Even if 'ts_bspline_to_beziers' fails, the state of 'in' has not been * * changed allowing you to handle the error properly. * * * * Unless stated otherwise, the order of the parameters for transformation * * functions is: * * * * function(input, [additional_input], output, [additional_output]) * * * * 'additional_input' are parameters required to calculate the actual * * transformation. 'additional_output' are parameters storing further result. * * * * Note: None of TinySpline's transformation functions frees the memory of the * * output parameter. Thus, when using the same output parameter multiple * * times, make sure to free memory before each call. Otherwise, you will * * have a bad time with memory leaks: * * * * tsBSpline in = ... // an arbitrary spline * * tsBSpline out; // result of transformations * * * * ts_bspline_to_beziers(&in, &out); // first transformation * * ... // some code * * ts_bspline_free(&out); // avoid memory leak. * * ts_bspline_buckle(&in, &out); // next transformation * * * * If you want to modify your input directly without having a separate output, * * pass it as input and output at once: * * * * tsBSpline s = ... // an arbitrary spline * * tsReal *knots = ... // a knot vector * * * * ts_bspline_set_knots(&s, knots, &s); // copy 'knots' into 's' * * * * Note: If a transformation function fails *and* input != output, all fields * * of the output parameter are set to 0/NULL. If input == output, your * * input may have an invalid state in case of errors. * * * ******************************************************************************/ /** * Computes the derivative of \p spline, see: * * http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-derv.html * * The derivative of a spline _s_ of degree _d_ with _m_ control points and * _n_ knots is another spline _s'_ of degree _d-1_ with _m-1_ control points * and _n-2_ knots, defined over _s_ as: * * \f{eqnarray*}{ * s'(u) &=& \sum_{i=0}^{n-1} N_{i+1,p-1}(u) * * (P_{i+1} - P_{i}) * p / (u_{i+p+1}-u_{i+1}) \\ * &=& \sum_{i=1}^{n} N_{i,p-1}(u) * * (P_{i} - P_{i-1}) * p / (u_{i+p}-u_{i}) * \f} * * If _s_ has a clamped knot vector, it can be shown that: * * \f{eqnarray*}{ * s'(u) &=& \sum_{i=0}^{n-1} N_{i,p-1}(u) * * (P_{i+1} - P_{i}) * p / (u_{i+p+1}-u_{i+1}) * \f} * * where the multiplicity of the first and the last knot value _u_ is _p_ * rather than _p+1_. * * On error, (and if \p spline != \p \_derivative\_) all values of * \p \_derivative\_ are set to 0/NULL. * * @param spline * The spline to derive. * @param \_derivative\_ * The output parameter storing the derivative of \p spline. * @return TS_SUCCESS * On success. * @return TS_UNDERIVABLE * If \p spline->deg < 1, \p spline->n_ctrlp < 2, or the multiplicity of * an internal knot of \p spline is greater than the degree of \p spline. * NOTE: This will be fixed in the future. * @return TS_MALLOC * If allocating memory failed. */ tsError ts_bspline_derive( const tsBSpline* spline, tsBSpline* _derivative_ ); /** * Creates a deep copy of \p spline (only if \p spline != \p \_result\_) and * copies the first \p spline->n_ctrlp * \p spline->dim values from \p ctrlp * to \p \_result\_->ctrlp using memmove. The behaviour of this function is * undefined, if the length of \p ctrlp is less than \p spline->n_ctrlp * * \p spline->dim. * * On error, (and if \p spline != \p \_result\_) all values of \p \_result\_ * are set to 0/NULL. * * @param spline * The spline to deep copy (if \p spline != \p \_result\_) and whose * control points are replaced with \p ctrlp. * @param ctrlp * The control points to copy to \p \_result\_->ctrlp. * @param \_result\_ * The output parameter storing the result of this function. * @return TS_SUCCESS * On success. * @return TS_MALLOC * If \p spline != \p \_result\_ and allocating memory failed. */ tsError ts_bspline_set_ctrlp( const tsBSpline* spline, const tsReal* ctrlp, tsBSpline* _result_ ); /** * Creates a deep copy of \p spline (only if \p spline != \p \_result\_) and * copies the the first \p spline->n_knots from \p knots to \p \_result\_ * using memmove. The behaviour of this function is undefined, if the length * of \p knots is less than \p spline->n_knots. * * On error, (and if \p spline != \p \_result\_) all values of \p \_result\_ * are set to 0/NULL. * * @param spline * The spline to deep copy (if \p spline != \p \_result\_) and whose * knots are replaced with \p knots. * @param knots * The knots to copy to \p \_result\_->knots. * @param \_result\_ * The output parameter storing the result of this function. * @return TS_SUCCESS * On success. * @return TS_MALLOC * If \p spline != \p \_result\_ and allocating memory failed. */ tsError ts_bspline_set_knots( const tsBSpline* spline, const tsReal* knots, tsBSpline* _result_ ); /** * Fills the knot vector of \p spline according to \p type with minimum knot * value \p min to maximum knot value \p max and stores the result in * \p \_result\_. Creates a deep copy of \p spline, if * \p spline != \p \_result\_. * * On error, (and if \p spline != \p \_result\_) all values of \p \_result\_ * are set to 0/NULL. * * @param spline * The spline to deep copy (if \p spline != \p \_result\_) and whose knot * vector is filled according to \p type with minimum knot value \p min * and maximum knot value \p max. * @param type * How to fill the knot vector of \p \_result\_. * @param min * The minimum knot value of the knot vector of \p \_result\_. * @param max * The maximum knot value of the knot vector of \p \_result\_. * @param \_result\_ * The output parameter storing the result of this function. * @return TS_SUCCESS * On success. * @return TS_DEG_GE_NCTRLP * If \p spline->n_knots < 2*(\p original->deg+1). We can reuse this * error code because \p spline->n_knots < 2*(\p spline->deg+1) implies * \p spline->deg >= \p spline->n_ctrlp. Furthermore, using * TS_DEG_GE_NCTRLP instead of TS_NUM_KNOTS ensures that TS_NUM_KNOTS is * not used twice for this function. To be more fail-safe, * \p spline->deg+1 instead of \p spline->order is used, to make sure * that \p spline->deg+1 >= 1. * @return TS_NUM_KNOTS * If \p type == TS_BEZIERS and * \p spline->n_knots % \p spline->order != 0. * @return TS_KNOTS_DECR * If \p min >= \p max. (::ts_fequals is used to determine whether * \p min == \p max). * @return TS_MALLOC * If \p spline != \p \_result\_ and allocating memory failed. */ tsError ts_bspline_fill_knots( const tsBSpline* spline, tsBSplineType type, tsReal min, tsReal max, tsBSpline* _result_ ); /** * Inserts the knot value \p u \p n times into \p spline and stores the result * in \p \_result\_. Creates a deep copy of \p spline, if * \p spline != \p \_result\_. * * On error, (and if \p spline != \p \_result\_) all values of \p \_result\_ * are set to 0/NULL. * * @param spline * The spline to deep copy (if \p spline != \p \_result\_) and whose knot * vector is extended with \p u \p n times. * @param u * The knot value to insert. * @param n * How many times \p u should be inserted. * @param \_result\_ * The output parameter storing the updated knot vector. * @param \_k\_ * The output parameter storing the last index of \p u in \p \_result\_. * @return TS_SUCCESS * On success. * @return TS_MALLOC * If \p spline != \p \_result\_ and allocating memory failed. */ tsError ts_bspline_insert_knot( const tsBSpline* spline, tsReal u, size_t n, tsBSpline* _result_, size_t* _k_ ); /** * Resizes \p spline by \p n (number of control points) and stores the result * in \p \_resized\_. Creates a deep copy of \p spline, if * \p spline != \p \_result\_. If \p back != 0 \p spline is resized at the * end. If \p back == 0 \p spline is resized at front. * * On error, (and if \p spline != \p \_result\_) all values of \p \_result\_ * are set to 0/NULL. * * @return TS_SUCCESS * On success. * @return TS_DEG_GE_NCTRLP * If the degree of \p \_resized\_ would be >= the number of the control * points of \p \_resized\_. * @return TS_DIM_ZERO * If \p spline != \p \_result\_ and \p spline->dim == 0. * @return TS_DEG_GE_NCTRLP * If \p spline != \p \_result\_ and * \p spline->deg >= \p spline->n_ctrlp. * @return TS_MALLOC * If \p spline != \p \_result\_ and allocating memory failed. */ tsError ts_bspline_resize( const tsBSpline* spline, int n, int back, tsBSpline* _resized_ ); /** * Splits \p spline at \p u and stores the result in \p \_split\_. That is, * \p u is inserted _n_ times such that s(\p u) == \p \_split\_->order. * Creates a deep copy of \p spline, if \p spline != \p \_split\_. * * On error, (and if \p spline != \p \_split\_) all values of \p \_split\_ * are set to 0/NULL. * * @param spline * The spline to deep copy (if \p spline != \p \_result\_) and split. * @param u * The split point. * @param \_split\_ * The output parameter storing the split spline. * @param \_k\_ * The output parameter storing the last index of \p u in \p \_split\_. * @return TS_SUCCESS * On success. * @return TS_MALLOC * If \p spline != \p \_split\_ and allocating memory failed. */ tsError ts_bspline_split( const tsBSpline* spline, tsReal u, tsBSpline* _split_, size_t* _k_ ); /** * Buckles \p spline by \p b and stores the result in \p \_buckled\_. Creates * a deep copy of \p spline, if \p spline != \p \_buckled\_. * * This function is based on: * * Holten, Danny. "Hierarchical edge bundles: Visualization of adjacency * relations in hierarchical data." Visualization and Computer Graphics, * IEEE Transactions on 12.5 (2006): 741-748. * * Holten calls it "straightening" (page 744, equation 1). * * Usually, the range of \p b is: 0.0 <= \p b <= 1.0 with 0 yielding to a line * connecting the first and the last control point (no buckle) and 1 keeping * the original shape (maximum buckle). If \b < 0 or \b > 1 the behaviour is * undefined, though, it will not result in an error. * * On error, (and if \p spline != \p \_buckled\_) all values of \p \_buckled\_ * are set to 0/NULL. * * @param spline * The spline to buckle by \p b. * @param b * The buckle factor (usually 0.0 <= \p b <= 1.0). * @param \_buckled\_ * The output parameter storing the buckled spline. * @return TS_SUCCESS * On success. * @return TS_MALLOC * If \p spline != \p \_buckled\_ and allocating memory failed. */ tsError ts_bspline_buckle( const tsBSpline* original, tsReal b, tsBSpline* _buckled_ ); /** * Subdivides \p spline into a sequence of Bezier curvs by splitting it at * each internal knot value. Creates a deep copy of \p spline, if * \p spline != \p \_beziers\_. * * On error, (and if \p spline != \p \_beziers\_) all values of \p \_beziers\_ * are set to 0/NULL. * * @param spline * The spline to subdivide into a sequence of Bezier curves. * @param \_beziers\_ * The output parameter storing the sequence of Bezier curves. * @return TS_SUCCESS * On success. * @return TS_MALLOC * If \p spline != \p \_beizers\_ and allocating memory failed. */ tsError ts_bspline_to_beziers( const tsBSpline* spline, tsBSpline* _beziers_ ); /****************************************************************************** * * * Utility Functions * * * * The following section contains utility functions used by TinySpline which * * also may be helpful when working with this library. * * * ******************************************************************************/ /** * Compares the tsReal values \p x and \p y using an absolute and relative * epsilon environment. * * @param x * The x value to compare. * @param y * The y value to compare. * @return 1 * If \p x is equals to \p y. * @return 0 * Otherwise. */ int ts_fequals( tsReal x, tsReal y ); /** * Returns the error message associated to \p err. Returns "unknown error" if * \p err is no associated (indicating a bug) or is TS_SUCCESS (which is not * an actual error). */ const char* ts_enum_str( tsError err ); /** * Returns the error code associated to \p str or TS_SUCCESS if \p str is not * associated. Keep in mind that by concept "unknown error" is not associated, * though, TS_SUCCESS is returned. */ tsError ts_str_enum( const char* str ); /** * Fills the given array \p arr with \p val from \p arr+0 to \p arr+ \p num * (exclusive). */ void ts_arr_fill( tsReal* arr, size_t num, tsReal val ); /** * Returns the euclidean distance of \p x and \p y consisting of \p dim * components, respectively. * * @param x * The x value. * @param y * The y value. * @param dim * The dimension of \p x and \p y. * @return * The euclidean distanc of \p x and \p y. */ tsReal ts_ctrlp_dist2( const tsReal* x, const tsReal* y, size_t dim ); #ifdef __cplusplus } #endif #endif /* TINYSPLINE_H */