2887 lines
95 KiB
C
2887 lines
95 KiB
C
/** @file */
|
|
|
|
#ifndef TINYSPLINE_H
|
|
#define TINYSPLINE_H
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
|
|
/*! @name Deprecation
|
|
*
|
|
* The macro \c TS_DEPRECATED can be used to mark functions as
|
|
* deprecated.
|
|
*
|
|
* @{
|
|
*/
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
#define TS_DEPRECATED __attribute__((deprecated))
|
|
#elif defined(_MSC_VER)
|
|
#define TS_DEPRECATED __declspec(deprecated)
|
|
#elif defined(SWIG)
|
|
#define TS_DEPRECATED
|
|
#else
|
|
#warning "WARNING: TS_DEPRECATED is not supported by the compiler"
|
|
#define TS_DEPRECATED
|
|
#endif
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name Library Export/Import
|
|
*
|
|
* If TinySpline is built for Windows, the macros \c TINYSPLINE_SHARED_EXPORT
|
|
* and \c TINYSPLINE_SHARED_IMPORT define the Microsoft specific directives \c
|
|
* __declspec(dllexport) and \c __declspec(dllimport), respectively. More
|
|
* information on these directives can be found at:
|
|
*
|
|
* https://docs.microsoft.com/en-us/cpp/cpp/dllexport-dllimport
|
|
*
|
|
* If TinySpline is built to the ELF (most Unix like environments) or Mach (OS
|
|
* X) object format, \c TINYSPLINE_SHARED_EXPORT defines the directive
|
|
* <tt>__attribute__ ((visibility ("default")))</tt> which, in combination with
|
|
* \c -fvisibility=hidden, behaves similar to \c __declspec(dllexport). \c
|
|
* TINYSPLINE_SHARED_IMPORT is set empty (i.e., it defines nothing).
|
|
*
|
|
* If none of the above applies, \c TINYSPLINE_SHARED_EXPORT and \c
|
|
* TINYSPLINE_SHARED_IMPORT are set empty (i.e., they define nothing).
|
|
*
|
|
* Depending on whether TinySpline is compiled <em>as shared library</em> or
|
|
* linked against <em>as shared library</em>, \c TINYSPLINE_API points to \c
|
|
* TINYSPLINE_SHARED_EXPORT (compiled) or \c TINYSPLINE_SHARED_IMPORT (linked
|
|
* against). All elements of TinySpline that needs to be exported/imported are
|
|
* annotated with \c TINYSPLINE_API. This eliminates the need for a
|
|
* module-definition (.def) file. If TinySpline is compiled or linked against
|
|
* <em>as static library</em>, \c TINYSPLINE_API is set empty (i.e., it defines
|
|
* nothing).
|
|
*
|
|
* If you consume TinySpline as shared library built for Windows, all you need
|
|
* is to define \c TINYSPLINE_SHARED. This will automatically import all
|
|
* required symbols. When compiling TinySpline, the build system should set all
|
|
* necessary defines.
|
|
*
|
|
* @{
|
|
*/
|
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
|
#define TINYSPLINE_SHARED_EXPORT __declspec(dllexport)
|
|
#define TINYSPLINE_SHARED_IMPORT __declspec(dllimport)
|
|
#elif defined(__ELF__) || defined(__MACH__)
|
|
#define TINYSPLINE_SHARED_EXPORT __attribute__ ((visibility ("default")))
|
|
#define TINYSPLINE_SHARED_IMPORT
|
|
#else
|
|
#define TINYSPLINE_SHARED_EXPORT
|
|
#define TINYSPLINE_SHARED_IMPORT
|
|
#endif
|
|
|
|
#ifdef TINYSPLINE_SHARED
|
|
#ifdef TINYSPLINE_EXPORT
|
|
#define TINYSPLINE_API TINYSPLINE_SHARED_EXPORT
|
|
#else
|
|
#define TINYSPLINE_API TINYSPLINE_SHARED_IMPORT
|
|
#endif
|
|
#else
|
|
#define TINYSPLINE_API
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name Predefined Constants
|
|
*
|
|
* The following constants have been adjusted to maintain internal consistency
|
|
* and should only be changed with great caution! The values chosen should be
|
|
* suitable for most environments and can be used with float (single) and
|
|
* double precision (see ::tsReal). If changes are necessary, please read the
|
|
* documentation of the constants in advance.
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* The mathematical constant pi.
|
|
*/
|
|
#define TS_PI 3.14159265358979323846
|
|
|
|
/**
|
|
* The maximum number of knots a spline can have. This constant is strongly
|
|
* related to ::TS_KNOT_EPSILON in that the larger ::TS_MAX_NUM_KNOTS is, the
|
|
* less precise ::TS_KNOT_EPSILON has to be (i.e., knots with greater distance
|
|
* are considered equal). Likewise, the more precise ::TS_KNOT_EPSILON is
|
|
* (i.e., knots with smaller distance are considered equal), the less
|
|
* ::TS_MAX_NUM_KNOTS has to be. By default, the relation between
|
|
* ::TS_MAX_NUM_KNOTS and ::TS_KNOT_EPSILON is as follows:
|
|
*
|
|
* TS_MAX_NUM_KNOTS = 1 / TS_KNOTS_EPSILON
|
|
*/
|
|
#define TS_MAX_NUM_KNOTS 10000
|
|
|
|
/**
|
|
* The minimum of the domain of newly created splines. Must be less than
|
|
* ::TS_DOMAIN_DEFAULT_MAX. This constant is used only when creating new
|
|
* splines. After creation, the domain of a spline can be adjusted as needed.
|
|
*/
|
|
#define TS_DOMAIN_DEFAULT_MIN 0.0f
|
|
|
|
/**
|
|
* The maximum of the domain of newly created splines. Must be greater than
|
|
* ::TS_DOMAIN_DEFAULT_MIN. This constant is used only when creating new
|
|
* splines. After creation, the domain of a spline can be adjusted as needed.
|
|
*/
|
|
#define TS_DOMAIN_DEFAULT_MAX 1.0f
|
|
|
|
/**
|
|
* If the distance between two knots falls below this threshold, they are
|
|
* considered equal. Must be positive ( > 0 ). This constant is strongly
|
|
* related to ::TS_MAX_NUM_KNOTS in that the more precise ::TS_KNOT_EPSILON is
|
|
* (i.e., knots with smaller distance are considered equal), the less
|
|
* ::TS_MAX_NUM_KNOTS has to be. Likewise, the larger ::TS_MAX_NUM_KNOTS is,
|
|
* the less precise ::TS_KNOT_EPSILON has to be (i.e., knots with greater
|
|
* distance are considered equal). By default, the relation between
|
|
* ::TS_KNOT_EPSILON and ::TS_MAX_NUM_KNOTS is as follows:
|
|
*
|
|
* TS_KNOT_EPSILON = 1 / TS_MAX_NUM_KNOTS
|
|
*
|
|
* It is recommended that ::TS_KNOT_EPSILON is aligned to the span of
|
|
* ::TS_DOMAIN_DEFAULT_MIN and ::TS_DOMAIN_DEFAULT_MAX. That is, adjacent
|
|
* floating point values in the domain [::TS_DOMAIN_DEFAULT_MIN,
|
|
* ::TS_DOMAIN_DEFAULT_MAX] should not be equal according to
|
|
* ::TS_KNOT_EPSILON. This is in particular recommended when ::TS_KNOT_EPSILON
|
|
* and ::TS_MAX_NUM_KNOTS are related to each other as described above.
|
|
*/
|
|
#define TS_KNOT_EPSILON 1e-4f
|
|
|
|
/**
|
|
* If the distance between two (control) points is less than or equal to this
|
|
* threshold, they are considered equal. This constant is not used directly by
|
|
* the C interface. Rather, it serves as a viable default value for functions
|
|
* requiring an epsilon environment to decide whether two (control) points are
|
|
* equal or not. The C++ interface, for example, uses this as default value for
|
|
* optional parameters.
|
|
*/
|
|
#ifdef TINYSPLINE_FLOAT_PRECISION
|
|
#define TS_POINT_EPSILON 1e-3f
|
|
#else
|
|
#define TS_POINT_EPSILON 1e-5f
|
|
#endif
|
|
|
|
/**
|
|
* If the length of an element (e.g., a vector) is less than this threshold,
|
|
* the length is considered \c 0. Must be positive ( > 0 ).
|
|
*/
|
|
#ifdef TINYSPLINE_FLOAT_PRECISION
|
|
#define TS_LENGTH_ZERO 1e-3f
|
|
#else
|
|
#define TS_LENGTH_ZERO 1e-4f
|
|
#endif
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name API Configuration
|
|
*
|
|
* In the following section, different aspects of TinySpline's API can be
|
|
* configured (compile-time). It is recommended to configure the API by
|
|
* supplying the corresponding preprocessor definition(s). That said, there is
|
|
* nothing wrong with editing the source code directly.
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* TinySpline uses its own typedef for floating point numbers. Supported are
|
|
* floats (single precision) and doubles (double precision). By default,
|
|
* doubles are used. Note that this typedef affects the entire API (i.e., types
|
|
* are not mixed; all structs and functions rely only on tsReal). Float
|
|
* precision is primarily used in combination with GLUT because GLUT's API
|
|
* doesn't support doubles:
|
|
*
|
|
* https://www.glprogramming.com/red/chapter12.html
|
|
*
|
|
* Generally, double precision is the right choice. Floats are mainly supported
|
|
* for legacy reasons. Yet, floats are not considered deprecated! If necessary,
|
|
* tsReal can also be typedefed to any other floating point representation. In
|
|
* this case, make sure to adjust TS_MAX_NUM_KNOTS and TS_KNOT_EPSILON
|
|
* (cf. Section "Predefined Constants").
|
|
*/
|
|
#ifdef TINYSPLINE_FLOAT_PRECISION
|
|
typedef float tsReal;
|
|
#else
|
|
typedef double tsReal;
|
|
#endif
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name Error Handling
|
|
*
|
|
* There are three types of error handling in TinySpline.
|
|
*
|
|
* 1. Return value: Functions that can fail return a special error code
|
|
* (::tsError). If the error code is not \c 0 (::TS_SUCCESS), an error occurred
|
|
* during execution. For example:
|
|
*
|
|
* if ( ts_bspline_to_beziers(&spline, &beziers, NULL) ) {
|
|
* ... An error occurred ...
|
|
* }
|
|
*
|
|
* It is of course possible to check the actual type of error:
|
|
*
|
|
* tsError error = ts_bspline_to_beziers(&spline, &beziers, NULL);
|
|
* if (error == TS_MALLOC) {
|
|
* ... Out of memory ...
|
|
* } else if (error == ...
|
|
*
|
|
* This type of error handling is used in many C programs. The disadvantage
|
|
* is that there is no additional error message besides the error code (with
|
|
* which the cause of an error could be specified in more detail). Some
|
|
* libraries make do with global variables in which error messages are stored
|
|
* for later purpose (e.g., \a errno and \a strerror). Unfortunately,
|
|
* however, this approach (by design) is often not thread-safe. The second
|
|
* error handling option solves this issue.
|
|
*
|
|
* 2. ::tsStatus objects: Functions that can fail do not only return an error
|
|
* code, but also take a pointer to a ::tsStatus object as an optional
|
|
* parameter. In the event of an error, and if the supplied pointer is not
|
|
* NULL, the error message is stored in tsStatus#message and can be accessed by
|
|
* the caller. Using a ::tsStatus object, the example given in 1. can be
|
|
* modified as follows:
|
|
*
|
|
* tsStatus status;
|
|
* if ( ts_bspline_to_beziers(&spline, &beziers, &status) ) {
|
|
* status.code; // error code
|
|
* status.message; // error message
|
|
* }
|
|
*
|
|
* Note that ::tsStatus objects can be reused:
|
|
*
|
|
* tsStatus status;
|
|
* if ( ts_bspline_to_beziers(&spline, &beziers, &status) ) {
|
|
* ...
|
|
* }
|
|
* ...
|
|
* if ( ts_bspline_derive(&beziers, 1, 0.001, &beziers, &status) ) {
|
|
* ...
|
|
* }
|
|
*
|
|
* If you would like to use this type of error handling in your own functions
|
|
* (in particular the optional ::tsStatus parameter), you may wonder whether
|
|
* there is an easy way to return error codes and format error messages. This
|
|
* is where the macros ::TS_RETURN_0 -- ::TS_RETURN_4 come into play. They
|
|
* can, for example, be used as follows:
|
|
*
|
|
* tsError my_function(..., tsStatus *status, ...)
|
|
* {
|
|
* ...
|
|
* tsReal *points = (tsReal *) malloc(len * sizeof(tsReal));
|
|
* if (!points)
|
|
* TS_RETURN_0(status, TS_MALLOC, "out of memory")
|
|
* ...
|
|
* }
|
|
*
|
|
* The \c N in \c TS_RETURN_<N> denotes the number of format specifier in the
|
|
* supplied format string (cf. sprintf(char *, const char *, ... )).
|
|
*
|
|
* 3. Try-catch-finally blocks: TinySpline provides a set of macros that can be
|
|
* used when a complex control flow is necessary. The macros create a structure
|
|
* that is similar to the exception handling mechanisms of high-level languages
|
|
* such as C++. The basic structure is as follows:
|
|
*
|
|
* TS_TRY(try, error, status) // `status' may be NULL
|
|
* ...
|
|
* TS_CALL( try, error, ts_bspline_to_beziers(
|
|
* &spline, &beziers, status) )
|
|
* ...
|
|
* TS_CATCH(error)
|
|
* ... Executed in case of an error ...
|
|
* ... `error' (tsError) indicates the type of error.
|
|
* ... `status' (tsStatus) contains the error code and message ...
|
|
* TS_FINALLY
|
|
* ... Executed in any case ...
|
|
* TS_END_TRY
|
|
*
|
|
* ::TS_TRY and ::TS_END_TRY mark the boundaries of a try-catch-finally
|
|
* block. Every block has an identifier (name) that must be unique within a
|
|
* scope. The name of a block is set via the first argument of ::TS_TRY (\c
|
|
* try in the example listed above). The control flow of a try-catch-finally
|
|
* block is directed via the second and third argument of ::TS_TRY (\c error
|
|
* and \c status in the example listed above) and the utility macro
|
|
* ::TS_CALL. The second argument of ::TS_TRY, a ::tsError, is mandatory. The
|
|
* third argument of ::TS_TRY, a ::tsStatus object, is optional, that is, it
|
|
* may be \c NULL. ::TS_CALL serves as a wrapper for functions with return
|
|
* type ::tsError. If the called functions fails (more on that later),
|
|
* ::TS_CALL immediately jumps into the ::TS_CATCH section where \c error and
|
|
* \c status can be evaluated as needed (note that \c status may be \c
|
|
* NULL). The ::TS_FINALLY section is executed in any case and is in
|
|
* particularly helpful when resources (such as heap-memory, file-handles
|
|
* etc.) must be released.
|
|
*
|
|
* While ::TS_CALL can be used to wrap functions with return type ::tsError,
|
|
* sooner or later it will be necessary to delegate the failure of other
|
|
* kinds of functions (i.e., functions outside of TinySpline; e.g.,
|
|
* malloc(size_t)). This is the purpose of the ::TS_THROW_0 -- ::TS_THROW_4
|
|
* macros. It is not by coincidence that the signature of the \c TS_THROW_<N>
|
|
* macros is quite similar to that of the \c TS_RETURN_<N> macros. Both
|
|
* "macro groups" are used to report errors. The difference between \c
|
|
* TS_RETURN_<N> and TS_THROW_<N>, however, is that the former exits a
|
|
* function (i.e., a \c return statement is inserted by these macros) while
|
|
* the latter jumps into a catch block (the catch block to jump into is set
|
|
* via the first argument of \c TS_THROW_<N>):
|
|
*
|
|
* tsBSpline spline = ts_bspline_init();
|
|
* tsReal *points = NULL;
|
|
* TS_TRY(try, error, status)
|
|
* ...
|
|
* tsReal *points = (tsReal *) malloc(len * sizeof(tsReal));
|
|
* if (!points)
|
|
* TS_THROW_0(try, status, TS_MALLOC, "out of memory")
|
|
* ...
|
|
* TS_CALL( try, error, ts_bspline_interpolate_cubic_natural(
|
|
* points, len / dim, dim, &spline, status) )
|
|
* ...
|
|
* TS_CATCH(error)
|
|
* ... Log error message ...
|
|
* TS_FINALLY
|
|
* ts_bspline_free(&spline);
|
|
* if (points)
|
|
* free(points);
|
|
* TS_END_TRY
|
|
*
|
|
* In all likelihood, you are already familiar with this kind error
|
|
* handling. Actually, there are a plethora of examples available online
|
|
* showing how exception-like error handling can be implemented in C. What
|
|
* most of these examples have in common is that they suggest to wrap the
|
|
* functions \c setjmp and \c longjmp (see setjmp.h) with macros. While this
|
|
* undoubtedly is a clever trick, \c setjmp and \c longjmp have no viable
|
|
* (i.e, thread-safe) solution for propagating the cause of an error (in the
|
|
* form of a human-readable error message) back to the client of a
|
|
* library. Therefore, TinySpline implements try-catch-finally blocks with \c
|
|
* if statements, labels, and \c goto statements (TS_THROW_<N>).
|
|
*
|
|
* ::TS_TRY is flexible enough to be used in functions that are in turn
|
|
* embedded into TinySpline's error handling system:
|
|
*
|
|
* tsError my_function(..., tsStatus *status, ...)
|
|
* {
|
|
* tsError error;
|
|
* TS_TRY(try, error, status)
|
|
* ...
|
|
* TS_END_TRY
|
|
* return error;
|
|
* }
|
|
*
|
|
* as well as functions forming the root of a call stack that uses
|
|
* TinySpline's error handling system:
|
|
*
|
|
* tsStatus status;
|
|
* TS_TRY(try, status.code, &status)
|
|
* ...
|
|
* TS_END_TRY
|
|
*
|
|
* There is some utility macros that might be useful when dealing with
|
|
* try-catch-finally blocks:
|
|
*
|
|
* - ::TS_END_TRY_RETURN: Returns the supplied error code immediately after
|
|
* completing the corresponding block. Can be used as follows:
|
|
*
|
|
* tsError my_function(..., tsStatus *status, ...)
|
|
* {
|
|
* tsError error;
|
|
* TS_TRY(try, error, status)
|
|
* ...
|
|
* TS_END_TRY_RETURN(error)
|
|
* }
|
|
*
|
|
* - ::TS_END_TRY_ROE: Like ::TS_END_TRY_RETURN but returns the supplied
|
|
* error code, \c e, if \c e is not ::TS_SUCCESS (\c ROE means
|
|
* <b>R</b>eturn <b>O</b>n <b>E</b>rror). Can be used as follows:
|
|
*
|
|
* tsError my_function(..., tsStatus *status, ...)
|
|
* {
|
|
* tsError error;
|
|
* TS_TRY(try, error, status)
|
|
* ...
|
|
* TS_END_TRY_ROE(error)
|
|
* ... Additional code. The code is executed only if `error' is
|
|
* TS_SUCCESS, that is, if no error occurred in the try block
|
|
* above ...
|
|
* }
|
|
*
|
|
* - ::TS_CALL_ROE: Calls the supplied function and returns its error code,
|
|
* \c e, if \c e is not ::TS_SUCCESS. This macro can be seen as a \e
|
|
* minified try block (a dedicated try block is not needed).
|
|
*
|
|
* - ::TS_RETURN_SUCCESS: Shortcut for ::TS_RETURN_0 with error code
|
|
* ::TS_SUCCESS and an empty error message.
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Defines different error codes.
|
|
*/
|
|
typedef enum
|
|
{
|
|
/** No error. */
|
|
TS_SUCCESS = 0,
|
|
|
|
/** Memory cannot be allocated (malloc, realloc etc.). */
|
|
TS_MALLOC = -1,
|
|
|
|
/** Points have dimensionality 0. */
|
|
TS_DIM_ZERO = -2,
|
|
|
|
/** degree >= num(control_points). */
|
|
TS_DEG_GE_NCTRLP = -3,
|
|
|
|
/** Knot is not within the domain. */
|
|
TS_U_UNDEFINED = -4,
|
|
|
|
/** multiplicity(knot) > order */
|
|
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,
|
|
|
|
/** len(control_points) % dimension != 0. */
|
|
TS_LCTRLP_DIM_MISMATCH = -10,
|
|
|
|
/** Error while reading/writing a file. */
|
|
TS_IO_ERROR = -11,
|
|
|
|
/** Error while parsing a serialized entity. */
|
|
TS_PARSE_ERROR = -12,
|
|
|
|
/** Index does not exist (e.g., when accessing an array). */
|
|
TS_INDEX_ERROR = -13,
|
|
|
|
/** Function returns without result (e.g., approximations). */
|
|
TS_NO_RESULT = -14,
|
|
|
|
/** Unexpected number of points. */
|
|
TS_NUM_POINTS = -15
|
|
} tsError;
|
|
|
|
/**
|
|
* Stores an error code (see ::tsError) with corresponding message.
|
|
*/
|
|
typedef struct
|
|
{
|
|
/** The error code. */
|
|
tsError code;
|
|
|
|
/**
|
|
* The corresponding error message (encoded as C string). Memory is
|
|
* allocated on stack so as to be able to provide a meaningful message
|
|
* in the event of memory issues (cf. ::TS_MALLOC).
|
|
*/
|
|
char message[100];
|
|
} tsStatus;
|
|
|
|
#define TS_TRY(label, error, status) \
|
|
{ \
|
|
(error) = TS_SUCCESS; \
|
|
if ((status) != NULL) { \
|
|
(status)->code = TS_SUCCESS; \
|
|
(status)->message[0] = '\0'; \
|
|
} \
|
|
__ ## label ## __: \
|
|
if (!(error)) {
|
|
|
|
#define TS_CALL(label, error, call) \
|
|
(error) = (call); \
|
|
if ((error)) goto __ ## label ## __;
|
|
|
|
#define TS_CATCH(error) \
|
|
} if ((error)) {
|
|
|
|
#define TS_FINALLY \
|
|
} {
|
|
|
|
#define TS_END_TRY \
|
|
} \
|
|
}
|
|
|
|
#define TS_END_TRY_RETURN(error) \
|
|
TS_END_TRY return (error);
|
|
|
|
#define TS_END_TRY_ROE(error) \
|
|
TS_END_TRY if ((error)) return error;
|
|
|
|
#define TS_CALL_ROE(error, call) \
|
|
{ \
|
|
(error) = (call); \
|
|
if ((error)) return error; \
|
|
}
|
|
|
|
#define TS_RETURN_SUCCESS(status) \
|
|
{ \
|
|
if ((status) != NULL) { \
|
|
(status)->code = TS_SUCCESS; \
|
|
(status)->message[0] = '\0'; \
|
|
} \
|
|
return TS_SUCCESS; \
|
|
}
|
|
|
|
#define TS_RETURN_0(status, error, msg) \
|
|
{ \
|
|
if ((status) != NULL) { \
|
|
(status)->code = error; \
|
|
sprintf((status)->message, msg); \
|
|
} \
|
|
return error; \
|
|
}
|
|
|
|
#define TS_RETURN_1(status, error, msg, arg1) \
|
|
{ \
|
|
if ((status) != NULL) { \
|
|
(status)->code = error; \
|
|
sprintf((status)->message, msg, arg1); \
|
|
} \
|
|
return error; \
|
|
}
|
|
|
|
#define TS_RETURN_2(status, error, msg, arg1, arg2) \
|
|
{ \
|
|
if ((status) != NULL) { \
|
|
(status)->code = error; \
|
|
sprintf((status)->message, msg, arg1, arg2); \
|
|
} \
|
|
return error; \
|
|
}
|
|
|
|
#define TS_RETURN_3(status, error, msg, arg1, arg2, arg3) \
|
|
{ \
|
|
if ((status) != NULL) { \
|
|
(status)->code = error; \
|
|
sprintf((status)->message, msg, arg1, arg2, arg3); \
|
|
} \
|
|
return error; \
|
|
}
|
|
|
|
#define TS_RETURN_4(status, error, msg, arg1, arg2, arg3, arg4) \
|
|
{ \
|
|
if ((status) != NULL) { \
|
|
(status)->code = error; \
|
|
sprintf((status)->message, msg, arg1, arg2, arg3, arg4); \
|
|
} \
|
|
return error; \
|
|
}
|
|
|
|
#define TS_THROW_0(label, error, status, val, msg) \
|
|
{ \
|
|
(error) = val; \
|
|
if ((status) != NULL) { \
|
|
(status)->code = val; \
|
|
sprintf((status)->message, msg); \
|
|
} \
|
|
goto __ ## label ## __; \
|
|
}
|
|
|
|
#define TS_THROW_1(label, error, status, val, msg, arg1) \
|
|
{ \
|
|
(error) = val; \
|
|
if ((status) != NULL) { \
|
|
(status)->code = val; \
|
|
sprintf((status)->message, msg, arg1); \
|
|
} \
|
|
goto __ ## label ## __; \
|
|
}
|
|
|
|
#define TS_THROW_2(label, error, status, val, msg, arg1, arg2) \
|
|
{ \
|
|
(error) = val; \
|
|
if ((status) != NULL) { \
|
|
(status)->code = val; \
|
|
sprintf((status)->message, msg, arg1, arg2); \
|
|
} \
|
|
goto __ ## label ## __; \
|
|
}
|
|
|
|
#define TS_THROW_3(label, error, status, val, msg, arg1, arg2, arg3) \
|
|
{ \
|
|
(error) = val; \
|
|
if ((status) != NULL) { \
|
|
(status)->code = val; \
|
|
sprintf((status)->message, msg, arg1, arg2, arg3); \
|
|
} \
|
|
goto __ ## label ## __; \
|
|
}
|
|
|
|
#define TS_THROW_4(label, error, status, val, msg, arg1, arg2, arg3, arg4) \
|
|
{ \
|
|
(error) = val; \
|
|
if ((status) != NULL) { \
|
|
(status)->code = val; \
|
|
sprintf((status)->message, msg, arg1, arg2, arg3, arg4); \
|
|
} \
|
|
goto __ ## label ## __; \
|
|
}
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name Utility Structs and Enums
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Describes the structure of the knot vector. More details can be found at:
|
|
*
|
|
* www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve.html
|
|
*/
|
|
typedef enum
|
|
{
|
|
/** Uniformly spaced knot vector with opened end knots. */
|
|
TS_OPENED = 0,
|
|
|
|
/** Uniformly spaced knot vector with clamped end knots. */
|
|
TS_CLAMPED = 1,
|
|
|
|
/**
|
|
* Uniformly spaced knot vector where the multiplicity of each knot is
|
|
* equal to the order of the spline.
|
|
*/
|
|
TS_BEZIERS = 2
|
|
} tsBSplineType;
|
|
|
|
/**
|
|
* A three-dimensional TNB-vector with position. More details can be found at:
|
|
*
|
|
* https://en.wikipedia.org/wiki/Frenet-Serret_formulas
|
|
* https://www.math.tamu.edu/~tkiffe/calc3/tnb/tnb.html
|
|
*
|
|
* TNB stands for \e tangent, \e normal, and \e binormal.
|
|
*/
|
|
typedef struct
|
|
{
|
|
/** Position of the TNB-vector. */
|
|
tsReal position[3];
|
|
|
|
/** Tangent of the TNB-vector. */
|
|
tsReal tangent[3];
|
|
|
|
/** Normal of the TNB-vector. */
|
|
tsReal normal[3];
|
|
|
|
/** Binormal of the TNB-vector. */
|
|
tsReal binormal[3];
|
|
} tsFrame;
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name B-Spline Data
|
|
*
|
|
* The internal state of ::tsBSpline is protected using the PIMPL design
|
|
* pattern (see https://en.cppreference.com/w/cpp/language/pimpl for more
|
|
* details). The data of an instance can be accessed with the functions listed
|
|
* in this section.
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Represents a B-Spline, which may also be used for NURBS, Bezier curves,
|
|
* lines, and points. NURBS use homogeneous coordinates to store their control
|
|
* points (i.e. the last component of a control point stores the weight).
|
|
* Bezier curves are B-Splines with 'num_control_points == order' and a
|
|
* clamped knot vector, which lets them pass through their first and last
|
|
* control point (a property which does not necessarily apply to B-Splines and
|
|
* NURBS). Lines and points, on that basis, are Bezier curves of degree 1
|
|
* (lines) and 0 (points).
|
|
*
|
|
* Two dimensional control points are stored as follows:
|
|
*
|
|
* [x_0, y_0, x_1, y_1, ..., x_n-1, y_n-1]
|
|
*
|
|
* Tree dimensional control points are stored 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. As already mentioned, NURBS use homogeneous coordinates to
|
|
* store their control points. For example, a NURBS in 2D stores its control
|
|
* points as follows:
|
|
*
|
|
* [x_0*w_0, y_0*w_0, w_0, x_1*w_1, y_1*w_1, w_1, ...]
|
|
*
|
|
* where 'w_i' is the weight of the i'th control point.
|
|
*/
|
|
typedef struct
|
|
{
|
|
struct tsBSplineImpl *pImpl; /**< The actual implementation. */
|
|
} tsBSpline;
|
|
|
|
/**
|
|
* Returns the degree of \p spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose degree is read.
|
|
* @return
|
|
* The degree of \p spline.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_bspline_degree(const tsBSpline *spline);
|
|
|
|
/**
|
|
* Returns the order (degree + 1) of \p spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose order is read.
|
|
* @return
|
|
* The order of \p spline.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_bspline_order(const tsBSpline *spline);
|
|
|
|
/**
|
|
* Returns the dimensionality of \p spline, that is, the number of components
|
|
* of its control points (::ts_bspline_control_points). One-dimensional splines
|
|
* are possible, albeit their benefit might be questionable.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose dimension is read.
|
|
* @return
|
|
* The dimension of \p spline (>= 1).
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_bspline_dimension(const tsBSpline *spline);
|
|
|
|
/**
|
|
* Returns the length of the control point array of \p spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose length of the control point array is read.
|
|
* @return
|
|
* The length of the control point array of \p spline.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_bspline_len_control_points(const tsBSpline *spline);
|
|
|
|
/**
|
|
* Returns the number of control points of \p spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose number of control points is read.
|
|
* @return
|
|
* The number of control points of \p spline.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_bspline_num_control_points(const tsBSpline *spline);
|
|
|
|
/**
|
|
* Returns the size of the control point array of \p spline. This function may
|
|
* be useful when copying control points using \e memcpy or \e memmove.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose size of the control point array is read.
|
|
* @return
|
|
* The size of the control point array of \p spline.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_bspline_sof_control_points(const tsBSpline *spline);
|
|
|
|
/**
|
|
* Returns the pointer to the control point array of \p spline. Note that the
|
|
* return type of this function is \c const for a reason. Clients should only
|
|
* read the returned array. When suppressing the constness and writing to the
|
|
* array against better knowledge, the client is on its own with regard to the
|
|
* consistency of the internal state of \p spline. If the control points of a
|
|
* spline need to be changed, use ::ts_bspline_control_points to obtain a copy
|
|
* of the control point array and ::ts_bspline_set_control_points to copy the
|
|
* changed values back to the spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose pointer to the control point array is returned.
|
|
* @return
|
|
* Pointer to the control point array of \p spline.
|
|
*/
|
|
const tsReal TINYSPLINE_API *
|
|
ts_bspline_control_points_ptr(const tsBSpline *spline);
|
|
|
|
/**
|
|
* Returns a deep copy of the control points of \p spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose control points are read.
|
|
* @param[out] ctrlp
|
|
* The output array. \b Note: It is the responsibility of the client to
|
|
* release the allocated memory after use.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_control_points(const tsBSpline *spline,
|
|
tsReal **ctrlp,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Returns the pointer to the control point of \p spline at \p index. Note that
|
|
* the type of the out parameter \p ctrlp is \c const for a reason. Clients
|
|
* should only read the returned array. When suppressing the constness of \p
|
|
* ctrlp and writing to the array against better knowledge, the client is on
|
|
* its own with regard to the consistency of the internal state of \p
|
|
* spline. If one of the control points of a spline needs to be changed, use
|
|
* ::ts_bspline_set_control_points to copy the new control point to the spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose pointer to the control point at \p index is returned.
|
|
* @param[in] index
|
|
* Zero-based index of the control point to be returned.
|
|
* @param[out] ctrlp
|
|
* Pointer to the control point of \p spline at \p index.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_INDEX_ERROR
|
|
* If \p index is out of range.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_control_point_at_ptr(const tsBSpline *spline,
|
|
size_t index,
|
|
const tsReal **ctrlp,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Sets the control points of \p spline. Creates a deep copy of \p ctrlp.
|
|
*
|
|
* @pre
|
|
* \p ctrlp has length ::ts_bspline_len_control_points.
|
|
* @param[out] spline
|
|
* The spline whose control points are set.
|
|
* @param[in] ctrlp
|
|
* The value.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_set_control_points(tsBSpline *spline,
|
|
const tsReal *ctrlp,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Sets the control point of \p spline at \p index. Creates a deep copy of
|
|
* \p ctrlp.
|
|
*
|
|
* @pre
|
|
* \p ctrlp has length ::ts_bspline_dimension.
|
|
* @param[out] spline
|
|
* The spline whose control point is set at \p index.
|
|
* @param[in] index
|
|
* Zero-based index of the control point to be set.
|
|
* @param[in] ctrlp
|
|
* The value.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_INDEX_ERROR
|
|
* If \p index is out of range.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_set_control_point_at(tsBSpline *spline,
|
|
size_t index,
|
|
const tsReal *ctrlp,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Returns the number of knots of \p spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose number of knots is read.
|
|
* @return
|
|
* The number of knots of \p spline.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_bspline_num_knots(const tsBSpline *spline);
|
|
|
|
/**
|
|
* Returns the size of the knot array of \p spline. This function may be useful
|
|
* when copying knots using \e memcpy or \e memmove.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose size of the knot array is read.
|
|
* @return TS_SUCCESS
|
|
* The size of the knot array of \p spline.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_bspline_sof_knots(const tsBSpline *spline);
|
|
|
|
/**
|
|
* Returns the pointer to the knot vector of \p spline. Note that the return
|
|
* type of this function is \c const for a reason. Clients should only read the
|
|
* returned array. When suppressing the constness and writing to the array
|
|
* against better knowledge, the client is on its own with regard to the
|
|
* consistency of the internal state of \p spline. If the knot vector of a
|
|
* spline needs to be changed, use ::ts_bspline_knots to obtain a copy of the
|
|
* knot vector and ::ts_bspline_set_knots to copy the changed values back to
|
|
* the spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose pointer to the knot vector is returned.
|
|
* @return
|
|
* Pointer to the knot vector of \p spline.
|
|
*/
|
|
const tsReal TINYSPLINE_API *
|
|
ts_bspline_knots_ptr(const tsBSpline *spline);
|
|
|
|
/**
|
|
* Returns a deep copy of the knots of \p spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose knots are read.
|
|
* @param[out] knots
|
|
* The output array. \b Note: It is the responsibility of the client to
|
|
* release the allocated memory after use.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_knots(const tsBSpline *spline,
|
|
tsReal **knots,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Returns the knot of \p spline at \p index.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose knot is read at \p index.
|
|
* @param[in] index
|
|
* Zero-based index of the knot to be read.
|
|
* @param[out] knot
|
|
* The output value.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_INDEX_ERROR
|
|
* If \p index is out of range.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_knot_at(const tsBSpline *spline,
|
|
size_t index,
|
|
tsReal *knot,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Sets the knots of \p spline. Creates a deep copy of \p knots.
|
|
*
|
|
* @pre
|
|
* \p knots has length ::ts_bspline_num_knots.
|
|
* @param[out] spline
|
|
* The spline whose knots are set.
|
|
* @param[in] knots
|
|
* The value.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_KNOTS_DECR
|
|
* If the knot vector is decreasing.
|
|
* @return TS_MULTIPLICITY
|
|
* If there is a knot with multiplicity > order
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_set_knots(tsBSpline *spline,
|
|
const tsReal *knots,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Sets the knots of \p spline supplied as varargs. As all splines have at
|
|
* least two knots, the first two knots have a named parameter. Note that, by
|
|
* design of varargs in C, the last named parameter must not be float. Thus,
|
|
* \p knot1 is of type double instead of ::tsReal.
|
|
*
|
|
* @pre
|
|
* ::ts_bspline_num_knots knots are supplied as varargs.
|
|
* @param[out] spline
|
|
* The spline whose knots are set.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @param[in] knot0
|
|
* The first knot.
|
|
* @param[in] knot1
|
|
* the second knot.
|
|
* @param[in] ...
|
|
* The remaining knots.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
* @return TS_KNOTS_DECR
|
|
* If the knot vector is decreasing.
|
|
* @return TS_MULTIPLICITY
|
|
* If there is a knot with multiplicity > order
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_set_knots_varargs(tsBSpline *spline,
|
|
tsStatus *status,
|
|
tsReal knot0,
|
|
double knot1,
|
|
...);
|
|
|
|
/**
|
|
* Sets the knot of \p spline at \p index.
|
|
*
|
|
* @param[in] spline
|
|
* The spline whose knot is set at \p index.
|
|
* @param[in] index
|
|
* Zero-based index of the knot to be set.
|
|
* @param[in] knot
|
|
* The value.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_INDEX_ERROR
|
|
* If \p index is out of range.
|
|
* @return TS_KNOTS_DECR
|
|
* If setting the knot at \p index results in a decreasing knot vector.
|
|
* @return TS_MULTIPLICITY
|
|
* If setting the knot at \p index results in a knot vector containing
|
|
* \p knot with multiplicity greater than the order of \p spline.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_set_knot_at(tsBSpline *spline,
|
|
size_t index,
|
|
tsReal knot,
|
|
tsStatus *status);
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name B-Spline Initialization
|
|
*
|
|
* The following functions are used to create and release ::tsBSpline instances
|
|
* as well as to copy and move the internal data of a ::tsBSpline instance to
|
|
* another instance.
|
|
*
|
|
* \b Note: It is recommended to initialize an instance with
|
|
* ::ts_bspline_init. This way, ::ts_bspline_free can be called in ::TS_CATCH
|
|
* and ::TS_FINALLY blocks (see Section Error Handling for more details)
|
|
* without further checking. For example:
|
|
*
|
|
* tsBSpline spline = ts_bspline_init();
|
|
* TS_TRY(...)
|
|
* ...
|
|
* TS_FINALLY
|
|
* ts_bspline_free(&spline);
|
|
* TS_END_TRY
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Creates a new spline whose data points to NULL.
|
|
*
|
|
* @return
|
|
* A new spline whose data points to NULL.
|
|
*/
|
|
tsBSpline TINYSPLINE_API
|
|
ts_bspline_init(void);
|
|
|
|
/**
|
|
* Creates a new spline and stores the result in \p spline.
|
|
*
|
|
* @param[in] num_control_points
|
|
* The number of control points of \p spline.
|
|
* @param[in] dimension
|
|
* The dimension of the control points of \p spline.
|
|
* @param[in] degree
|
|
* The degree of \p spline.
|
|
* @param[in] type
|
|
* How to setup the knot vector of \p spline.
|
|
* @param[out] spline
|
|
* The output spline.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_DIM_ZERO
|
|
* If \p dimension is \c 0.
|
|
* @return TS_DEG_GE_NCTRLP
|
|
* If \p degree >= \p num_control_points.
|
|
* @return TS_NUM_KNOTS
|
|
* If \p type is ::TS_BEZIERS and
|
|
* (\p num_control_points % \p degree + 1) != 0.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_new(size_t num_control_points,
|
|
size_t dimension,
|
|
size_t degree,
|
|
tsBSplineType type,
|
|
tsBSpline *spline,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Creates a new spline with given control points (varargs) and stores the
|
|
* result in \p spline. As all splines have at least one control point (with
|
|
* minimum dimensionality one), the first component of the first control point
|
|
* has a named parameter. Note that, by design of varargs in C, the last named
|
|
* parameter must not be float. Thus, \p first is of type double instead of
|
|
* ::tsReal.
|
|
*
|
|
* @param[in] num_control_points
|
|
* The number of control points of \p spline.
|
|
* @param[in] dimension
|
|
* The dimension of the control points of \p spline.
|
|
* @param[in] degree
|
|
* The degree of \p spline.
|
|
* @param[in] type
|
|
* How to setup the knot vector of \p spline.
|
|
* @param[out] spline
|
|
* The output spline.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @param[in] first
|
|
* The first component of the first control point.
|
|
* @param[in] ...
|
|
* The remaining components (control points).
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_DIM_ZERO
|
|
* If \p dimension is \c 0.
|
|
* @return TS_DEG_GE_NCTRLP
|
|
* If \p degree >= \p num_control_points.
|
|
* @return TS_NUM_KNOTS
|
|
* If \p type is ::TS_BEZIERS and
|
|
* (\p num_control_points % \p degree + 1) != 0.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_new_with_control_points(size_t num_control_points,
|
|
size_t dimension,
|
|
size_t degree,
|
|
tsBSplineType type,
|
|
tsBSpline *spline,
|
|
tsStatus *status,
|
|
double first,
|
|
...);
|
|
|
|
/**
|
|
* Creates a deep copy of \p src and stores the copied data in \p dest. \p src
|
|
* and \p dest can be the same instance.
|
|
*
|
|
* \b Note: Unlike \e memcpy and \e memmove, the first parameter is the source
|
|
* and the second parameter is the destination.
|
|
*
|
|
* @param[in] src
|
|
* The spline to be deep copied.
|
|
* @param[out] dest
|
|
* The output spline.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_copy(const tsBSpline *src,
|
|
tsBSpline *dest,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Moves the ownership of the data of \p src to \p dest. After calling this
|
|
* function, the data of \p src points to NULL. Does not release the data of \p
|
|
* dest. \p src and \p dest can be the same instance (in this case, the data of
|
|
* \p src remains).
|
|
*
|
|
* @param[in, out] src
|
|
* The spline whose data is moved to \p dest.
|
|
* @param[out] dest
|
|
* The spline that receives the data of \p src.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_bspline_move(tsBSpline *src,
|
|
tsBSpline *dest);
|
|
|
|
/**
|
|
* Releases the data of \p spline. After calling this function, the data of \p
|
|
* spline points to NULL.
|
|
*
|
|
* @param[out] spline
|
|
* The spline to be released.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_bspline_free(tsBSpline *spline);
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name De Boor Net Data
|
|
*
|
|
* The internal state of ::tsDeBoorNet is protected using the PIMPL design
|
|
* pattern (see https://en.cppreference.com/w/cpp/language/pimpl for more
|
|
* details). The data of an instance can be accessed with the functions listed
|
|
* in this section.
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Represents the output of De Boor's algorithm. De Boor's algorithm is used to
|
|
* evaluate a spline at a certain knot by iteratively computing a net of
|
|
* intermediate points until the resulting point 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 a net are stored in \c points (::ts_deboornet_points). The
|
|
* resulting point is the last point in \c points and, for the sake of
|
|
* convenience, can be accessed with ::ts_deboornet_result.
|
|
*
|
|
* Two dimensional points are stored as follows:
|
|
*
|
|
* [x_0, y_0, x_1, y_1, ..., x_n-1, y_n-1]
|
|
*
|
|
* Tree dimensional points are stored 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. The output also supports homogeneous coordinates
|
|
* (cf. ::tsBSpline).
|
|
*
|
|
* There is a special case in which the evaluation of a knot \c u returns two
|
|
* results instead of one. It occurs when the multiplicity of \c u (\c s(u)) is
|
|
* equals to the order of the evaluated spline, indicating that the spline is
|
|
* discontinuous at \c u. This is common practice for B-Splines (and NURBS)
|
|
* consisting of connected Bezier curves where the endpoint of curve \c c_i is
|
|
* equal to the start point of curve \c c_i+1. Yet, the end point of \c c_i and
|
|
* the start point of \c c_i+1 may still be completely different, yielding to
|
|
* visible gaps (if distance of the points is large enough). In such case (\c
|
|
* s(u) == \c order), ::ts_deboornet_points stores only the two resulting
|
|
* points (there is no net to calculate) and ::ts_deboornet_result points to
|
|
* the \e first point in ::ts_deboornet_points. Since having gaps in splines is
|
|
* unusual, both points in ::ts_deboornet_points are generally equal, making it
|
|
* easy to handle this special case by simply calling
|
|
* ::ts_deboornet_result. However, one can access both points if necessary:
|
|
*
|
|
* ts_deboornet_result(...)[0] ... // Access the first component of
|
|
* // the first result.
|
|
*
|
|
* ts_deboornet_result(...)[dim(spline)] // Access the first component of
|
|
* // the second result.
|
|
*
|
|
* As if this wasn't complicated enough, there is an exception for this special
|
|
* case, yielding to exactly one result (just like the regular case) even if \c
|
|
* s(u) == \c order. It occurs when \c u is the lower or upper bound of the
|
|
* domain of the evaluated spline. For instance, if \c b is a spline with
|
|
* domain [0, 1] and \c b is evaluated at \c u = \c 0 or \c u = \c 1, then
|
|
* ::ts_deboornet_result is \e always a single point regardless of the
|
|
* multiplicity of \c u.
|
|
*
|
|
* In summary, there are three different types of evaluation:
|
|
*
|
|
* 1. The regular case, in which all points of the net are returned.
|
|
*
|
|
* 2. A special case, in which two results are returned (required for splines
|
|
* with gaps).
|
|
*
|
|
* 3. The exception of 2., in which exactly one result is returned (even if \c
|
|
* s(u) == \c order).
|
|
*
|
|
* All in all this looks quite complex (and actually it is), but for most
|
|
* applications you do not have to deal with this. Just use
|
|
* ::ts_deboornet_result to access the outcome of De Boor's algorithm.
|
|
*/
|
|
typedef struct
|
|
{
|
|
struct tsDeBoorNetImpl *pImpl; /**< The actual implementation. */
|
|
} tsDeBoorNet;
|
|
|
|
/**
|
|
* Returns the knot (sometimes also referred to as \c u or \c t) of \p net.
|
|
*
|
|
* @param[in] net
|
|
* The net whose knot is read.
|
|
* @return
|
|
* The knot of \p net.
|
|
*/
|
|
tsReal TINYSPLINE_API
|
|
ts_deboornet_knot(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the index of the knot of \p net.
|
|
*
|
|
* @param[in] net
|
|
* The net whose index is read.
|
|
* @return
|
|
* The index [u_k, u_k+1) with \c u being the knot of \p net.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_deboornet_index(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the multiplicity of the knot of \p net.
|
|
*
|
|
* @param[in] net
|
|
* The net whose multiplicity is read.
|
|
* @return
|
|
* The multiplicity of the knot of \p net.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_deboornet_multiplicity(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the number of insertion that were necessary to evaluate the knot of
|
|
* \p net.
|
|
*
|
|
* @param[in] net
|
|
* The net whose number of insertions of its knot is read.
|
|
* @return
|
|
* The number of insertions that were necessary to evaluate the knot of \p
|
|
* net.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_deboornet_num_insertions(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the dimensionality of \p net, that is, the number of components of
|
|
* its points (::ts_deboornet_points) and result (::ts_deboornet_result).
|
|
* One-dimensional nets are possible, albeit their benefit might be
|
|
* questionable.
|
|
*
|
|
* @param[in] net
|
|
* The net whose dimension is read.
|
|
* @return
|
|
* The dimensionality of \p net (>= 1).
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_deboornet_dimension(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the length of the point array of \p net.
|
|
*
|
|
* @param[in] net
|
|
* The net whose length of the point array is read.
|
|
* @return
|
|
* The length of the point array of \p net.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_deboornet_len_points(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the number of points of \p net.
|
|
*
|
|
* @param[in] net
|
|
* The net whose number of points is read.
|
|
* @return
|
|
* The number of points of \p net.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_deboornet_num_points(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the size of the point array of \p net. This function may be useful
|
|
* when copying points using \e memcpy or \e memmove.
|
|
*
|
|
* @param[in] net
|
|
* The net whose size of the point array is read.
|
|
* @return
|
|
* The size of the point array of \p net.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_deboornet_sof_points(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the pointer to the point array of \p net. Note that the return type
|
|
* of this function is \c const for a reason. Clients should only read the
|
|
* returned array. When suppressing the constness and writing to the array
|
|
* against better knowledge, the client is on its own with regard to the
|
|
* consistency of the internal state of \p net. To obtain a copy of the points
|
|
* of \p net, use ::ts_deboornet_points.
|
|
*
|
|
* @param[in] net
|
|
* The net whose pointer to the point array is returned.
|
|
* @return
|
|
* Pointer to the point array of \p net.
|
|
*/
|
|
const tsReal TINYSPLINE_API *
|
|
ts_deboornet_points_ptr(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns a deep copy of the points of \p net.
|
|
*
|
|
* @param[in] net
|
|
* The net whose points are read.
|
|
* @param[out] points
|
|
* The output array. \b Note: It is the responsibility of the client to
|
|
* release the allocated memory after use.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_deboornet_points(const tsDeBoorNet *net,
|
|
tsReal **points,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Returns the length of the result array of \p net.
|
|
*
|
|
* @param[in] net
|
|
* The net whose length of the result array is read.
|
|
* @return
|
|
* The length of the result array of \p net.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_deboornet_len_result(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the number of points in the result array of \p net
|
|
* (1 <= num_result <= 2).
|
|
*
|
|
* @param[in] net
|
|
* The net whose number of points in the result array is read.
|
|
* @return
|
|
* The number of points in the result array of \p net.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_deboornet_num_result(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the size of the result array of \p net. This function may be useful
|
|
* when copying results using \e memcpy or \e memmove.
|
|
*
|
|
* @param[in] net
|
|
* The net whose size of the result array is read.
|
|
* @return TS_SUCCESS
|
|
* The size of the result array of \p net.
|
|
*/
|
|
size_t TINYSPLINE_API
|
|
ts_deboornet_sof_result(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns the pointer to the result array of \p net. Note that the return type
|
|
* of this function is \c const for a reason. Clients should only read the
|
|
* returned array. When suppressing the constness and writing to the array
|
|
* against better knowledge, the client is on its own with regard to the
|
|
* consistency of the internal state of \p net. To obtain a copy of the result
|
|
* of \p net, use ::ts_deboornet_result.
|
|
*
|
|
* @param[in] net
|
|
* The net whose pointer to the result array is returned.
|
|
* @return
|
|
* Pointer to the result array of \p net.
|
|
*/
|
|
const tsReal TINYSPLINE_API *
|
|
ts_deboornet_result_ptr(const tsDeBoorNet *net);
|
|
|
|
/**
|
|
* Returns a deep copy of the result of \p net.
|
|
*
|
|
* @param[in] net
|
|
* The net whose result is read.
|
|
* @param[out] result
|
|
* The output array. \b Note: It is the responsibility of the client to
|
|
* release the allocated memory after use.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_deboornet_result(const tsDeBoorNet *net,
|
|
tsReal **result,
|
|
tsStatus *status);
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name De Boor Net Initialization
|
|
*
|
|
* The following functions are used to create and release ::tsDeBoorNet
|
|
* instances as well as to copy and move the internal data of a ::tsDeBoorNet
|
|
* instance to another instance.
|
|
*
|
|
* \b Note: It is recommended to initialize an instance with
|
|
* ::ts_deboornet_init. This way, ::ts_deboornet_free can be called in
|
|
* ::TS_CATCH and ::TS_FINALLY blocks (see Section Error Handling for more
|
|
* details) without further checking. For example:
|
|
*
|
|
* tsDeBoorNet net = ts_deboornet_init();
|
|
* TS_TRY(...)
|
|
* ...
|
|
* TS_FINALLY
|
|
* ts_deboornet_free(&net);
|
|
* TS_END_TRY
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Creates a new net whose data points to NULL.
|
|
*
|
|
* @return
|
|
* A new net whose data points to NULL.
|
|
*/
|
|
tsDeBoorNet TINYSPLINE_API
|
|
ts_deboornet_init(void);
|
|
|
|
/**
|
|
* Creates a deep copy of \p src and stores the copied data in \p dest. \p src
|
|
* and \p dest can be the same instance.
|
|
*
|
|
* \b Note: Unlike \e memcpy and \e memmove, the first parameter is the source
|
|
* and the second parameter is the destination.
|
|
*
|
|
* @param[in] src
|
|
* The net to be deep copied.
|
|
* @param[out] dest
|
|
* The output net.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_deboornet_copy(const tsDeBoorNet *src,
|
|
tsDeBoorNet *dest,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Moves the ownership of the data of \p src to \p dest. After calling this
|
|
* function, the data of \p src points to NULL. Does not release the data of \p
|
|
* dest. \p src and \p dest can be the same instance (in this case, the data of
|
|
* \p src remains).
|
|
*
|
|
* @param[out] src
|
|
* The net whose data is moved to \p dest.
|
|
* @param[out] dest
|
|
* The net that receives the data of \p src.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_deboornet_move(tsDeBoorNet *src,
|
|
tsDeBoorNet *dest);
|
|
|
|
/**
|
|
* Releases the data of \p net. After calling this function, the data of \p net
|
|
* points to NULL.
|
|
*
|
|
* @param[out] net
|
|
* The net to be released.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_deboornet_free(tsDeBoorNet *net);
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name Interpolation and Approximation Functions
|
|
*
|
|
* Given a set (or a sequence) of points, interpolate/approximate a spline that
|
|
* follows these points.
|
|
*
|
|
* Note: Approximations have not yet been implemented. Pull requests are
|
|
* welcome.
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Interpolates a cubic spline with natural end conditions. For more details
|
|
* 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 interpolated spline is a sequence of bezier curves connecting each point
|
|
* in \p points. Each bezier curve is of degree \c 3 with dimensionality \p
|
|
* dimension. The total number of control points is:
|
|
*
|
|
* min(1, \p num_points - 1) * 4
|
|
*
|
|
* Note: \p num_points is the number of points in \p points and not the length
|
|
* of \p points. For instance, the following point vector has
|
|
* \p num_points = 4 and \p dimension = 2:
|
|
*
|
|
* [x0, y0, x1, y1, x2, y2, x3, y3]
|
|
*
|
|
* @param[in] points
|
|
* The points to be interpolated.
|
|
* @param[in] num_points
|
|
* The number of points in \p points. If \c 1, a cubic point (i.e., a
|
|
* spline with four times the same control point) is created.
|
|
* @param[in] dimension
|
|
* The dimensionality of the points.
|
|
* @param[out] spline
|
|
* The interpolated spline.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_DIM_ZERO
|
|
* If \p dimension is 0.
|
|
* @return TS_NUM_POINTS
|
|
* If \p num_points is 0.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_interpolate_cubic_natural(const tsReal *points,
|
|
size_t num_points,
|
|
size_t dimension,
|
|
tsBSpline *spline,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Interpolates a piecewise cubic spline by translating the given catmull-rom
|
|
* control points into a sequence of bezier curves. In order to avoid division
|
|
* by zero, successive control points with distance less than or equal to \p
|
|
* epsilon are filtered out. If the resultant sequence contains only a single
|
|
* point, a cubic point (i.e., a spline with four times the same control point)
|
|
* is created. Optionally, the first and last control point can be specified
|
|
* (see \p first and \p last).
|
|
*
|
|
* @param[in] points
|
|
* The points to be interpolated.
|
|
* @param[in] num_points
|
|
* The number of points in \p points. If \c 1, a cubic point (i.e., a
|
|
* spline with four times the same control point) is created.
|
|
* @param[in] dimension
|
|
* The dimensionality of the points.
|
|
* @param[in] alpha
|
|
* Knot parameterization: 0 => uniform, 0.5 => centripetal, 1 => chordal.
|
|
* The input value is clamped to the domain [0, 1].
|
|
* @param[in] first
|
|
* The first control point of the catmull-rom sequence. If NULL, an
|
|
* appropriate point is generated based on the first two points in
|
|
* \p points. If the distance between \p first and the first control point
|
|
* in \p points is less than or equals to \p epsilon, \p first is treated
|
|
* as NULL. This is necessary to avoid division by zero.
|
|
* @param[in] last
|
|
* The last control point of the catmull-rom sequence. If NULL, an
|
|
* appropriate point is generated based on the last two points in
|
|
* \p points. If the distance between \p last and the last control point
|
|
* in \p points is less than or equals to \p epsilon, \p last is treated
|
|
* as NULL. This is necessary to avoid division by zero.
|
|
* @param[in] epsilon
|
|
* The maximum distance between points with "same" coordinates. That is,
|
|
* if the distance between neighboring points is less than or equal to
|
|
* \p epsilon, they are considered equal. For the sake of fail-safeness,
|
|
* the sign is removed with fabs. It is advisable to pass a value greater
|
|
* than zero, however, it is not necessary.
|
|
* @param[out] spline
|
|
* The interpolated spline.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_DIM_ZERO
|
|
* If \p dimension is 0.
|
|
* @return TS_NUM_POINTS
|
|
* If \p num_points is 0.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_interpolate_catmull_rom(const tsReal *points,
|
|
size_t num_points,
|
|
size_t dimension,
|
|
tsReal alpha,
|
|
const tsReal *first,
|
|
const tsReal *last,
|
|
tsReal epsilon,
|
|
tsBSpline *spline,
|
|
tsStatus *status);
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name Query Functions
|
|
*
|
|
* Functions for querying different kinds of data from splines.
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Evaluates \p spline at \p knot and stores the result (see ::tsDeBoorNet) in
|
|
* \p net.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to evaluate.
|
|
* @param[in] knot
|
|
* The knot to evaluate \p spline at.
|
|
* @param[out] net
|
|
* Stores the evaluation result.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_U_UNDEFINED
|
|
* If \p spline is not defined at knot value \p u.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_eval(const tsBSpline *spline,
|
|
tsReal knot,
|
|
tsDeBoorNet *net,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Evaluates \p spline at each knot in \p knots and stores the evaluated points
|
|
* (see ::ts_deboornet_result) in \p points. If \p knots contains one or more
|
|
* knots where \p spline is discontinuous at, only the first point of the
|
|
* corresponding evaluation result is taken. After calling this function \p
|
|
* points contains exactly \code num * ts_bspline_dimension(spline) \endcode
|
|
* values.
|
|
*
|
|
* This function is in particular useful in cases where a multitude of knots
|
|
* need to be evaluated, because only a single instance of ::tsDeBoorNet is
|
|
* created and reused for all evaluation tasks (therefore the memory footprint
|
|
* is reduced to a minimum).
|
|
*
|
|
* @param[in] spline
|
|
* The spline to evaluate.
|
|
* @param[in] knots
|
|
* The knot values to evaluate.
|
|
* @param[in] num
|
|
* The number of knots in \p us.
|
|
* @param[out] points
|
|
* The output parameter.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_U_UNDEFINED
|
|
* If \p spline is not defined at one of the knot values in \p us.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_eval_all(const tsBSpline *spline,
|
|
const tsReal *knots,
|
|
size_t num,
|
|
tsReal **points,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Generates a sequence of \p num different knots, passes this sequence to
|
|
* ::ts_bspline_eval_all, and stores the resultant points in \p points. The
|
|
* sequence of knots is generated using ::ts_bspline_uniform_knot_seq. If \p
|
|
* num is 0, the default value \c 100 is used as fallback.
|
|
*
|
|
* For the sake of stability regarding future changes, the actual number of
|
|
* generated knots (which only differs from \p num if \p num is 0) is stored in
|
|
* \p actual_num. If \p num is 1, the point located at the minimum of the
|
|
* domain of \p spline is evaluated.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to be evaluate.
|
|
* @param[in] num
|
|
* The number of knots to be generate.
|
|
* @param[out] points
|
|
* The output parameter.
|
|
* @param[out] actual_num
|
|
* The actual number of generated knots. Differs from \p num only if
|
|
* \p num is 0. Must not be NULL.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_sample(const tsBSpline *spline,
|
|
size_t num,
|
|
tsReal **points,
|
|
size_t *actual_num,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Tries to find a point P on \p spline such that:
|
|
*
|
|
* ts_distance(P[index], value, 1) <= fabs(epsilon)
|
|
*
|
|
* This function is using the bisection method to determine P. Accordingly, it
|
|
* is expected that the control points of \p spline are sorted at component
|
|
* \p index either in ascending order (if \p ascending != 0) or in descending
|
|
* order (if \p ascending == 0). If the control points of \p spline are not
|
|
* sorted at component \p index, the behaviour of this function is undefined.
|
|
* For the sake of fail-safeness, the distance of P[index] and \p value is
|
|
* compared with the absolute value of \p epsilon (using fabs).
|
|
*
|
|
* The bisection method is an iterative approach which minimizes the error
|
|
* (\p epsilon) with each iteration step until an "optimum" was found. However,
|
|
* there may be no point P satisfying the distance condition. Thus, the number
|
|
* of iterations must be limited (\p max_iter). Depending on the domain of the
|
|
* control points of \p spline at component \p index and \p epsilon,
|
|
* \p max_iter ranges from 7 to 50. In most cases \p max_iter == 30 should be
|
|
* fine though. The parameter \p persnickety allows to define the behaviour of
|
|
* this function is case no point was found after \p max_iter iterations. If
|
|
* enabled (!= 0), TS_NO_RESULT is returned. If disabled (== 0), the best
|
|
* fitting point is returned.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to evaluate
|
|
* @param[in] value
|
|
* The value (point at component \p index) to find.
|
|
* @param[in] epsilon
|
|
* The maximum distance (inclusive).
|
|
* @param[in] persnickety
|
|
* Indicates whether TS_NO_RESULT should be returned if there is no point
|
|
* P satisfying the distance condition (!= 0 to enable, == 0 to disable).
|
|
* If disabled, the best fitting point is returned.
|
|
* @param[in] index
|
|
* The point's component.
|
|
* @param[in] ascending
|
|
* Indicates whether the control points of \p spline are sorted in
|
|
* ascending (!= 0) or in descending (== 0) order at component \p index.
|
|
* @param[in] max_iter
|
|
* The maximum number of iterations (30 is a sane default value).
|
|
* @param[out] net
|
|
* The output parameter.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_INDEX_ERROR
|
|
* If the dimension of the control points of \p spline <= \p index.
|
|
* @return TS_NO_RESULT
|
|
* If \p persnickety is enabled (!= 0) and there is no point P satisfying
|
|
* the distance condition.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_bisect(const tsBSpline *spline,
|
|
tsReal value,
|
|
tsReal epsilon,
|
|
int persnickety,
|
|
size_t index,
|
|
int ascending,
|
|
size_t max_iter,
|
|
tsDeBoorNet *net,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Returns the domain of \p spline.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to query.
|
|
* @param[out] min
|
|
* The lower bound of the domain of \p spline.
|
|
* @param[out] max
|
|
* The upper bound of the domain of \p spline.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_bspline_domain(const tsBSpline *spline,
|
|
tsReal *min,
|
|
tsReal *max);
|
|
|
|
/**
|
|
* Checks whether the distance of the endpoints of \p spline is less than or
|
|
* equal to \p epsilon for the first 'ts_bspline_degree - 1' derivatives
|
|
* (starting with the zeroth derivative).
|
|
*
|
|
* @param[in] spline
|
|
* The spline to query.
|
|
* @param[in] epsilon
|
|
* The maximum distance.
|
|
* @param[out] closed
|
|
* The output parameter. 1 if true, 0 otherwise.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_is_closed(const tsBSpline *spline,
|
|
tsReal epsilon,
|
|
int *closed,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Computes a sequence of three-dimensional frames (see ::tsFrame) for the
|
|
* spline \p spline. The position of the frames corresponds to the knots in \p
|
|
* knots. The implementation is based on:
|
|
*
|
|
* @article{10.1145/1330511.1330513,
|
|
* author = {Wang, Wenping and J\"{u}ttler, Bert and Zheng, Dayue
|
|
* and Liu, Yang},
|
|
* title = {Computation of Rotation Minimizing Frames},
|
|
* year = {2008},
|
|
* issue_date = {March 2008},
|
|
* publisher = {Association for Computing Machinery},
|
|
* address = {New York, NY, USA},
|
|
* volume = {27},
|
|
* number = {1},
|
|
* issn = {0730-0301},
|
|
* url = {https://doi.org/10.1145/1330511.1330513},
|
|
* doi = {10.1145/1330511.1330513},
|
|
* abstract = {Due to its minimal twist, the rotation minimizing
|
|
* frame (RMF) is widely used in computer graphics,
|
|
* including sweep or blending surface modeling, motion
|
|
* design and control in computer animation and
|
|
* robotics, streamline visualization, and tool path
|
|
* planning in CAD/CAM. We present a novel simple and
|
|
* efficient method for accurate and stable computation
|
|
* of RMF of a curve in 3D. This method, called the
|
|
* double reflection method, uses two reflections to
|
|
* compute each frame from its preceding one to yield a
|
|
* sequence of frames to approximate an exact RMF. The
|
|
* double reflection method has the fourth order global
|
|
* approximation error, thus it is much more accurate
|
|
* than the two currently prevailing methods with the
|
|
* second order approximation error—the projection
|
|
* method by Klok and the rotation method by
|
|
* Bloomenthal, while all these methods have nearly the
|
|
* same per-frame computational cost. Furthermore, the
|
|
* double reflection method is much simpler and faster
|
|
* than using the standard fourth order Runge-Kutta
|
|
* method to integrate the defining ODE of the RMF,
|
|
* though they have the same accuracy. We also
|
|
* investigate further properties and extensions of the
|
|
* double reflection method, and discuss the
|
|
* variational principles in design moving frames with
|
|
* boundary conditions, based on RMF.},
|
|
* journal = {ACM Trans. Graph.},
|
|
* month = mar,
|
|
* articleno = {2},
|
|
* numpages = {18},
|
|
* keywords = {motion design, sweep surface, motion, differential
|
|
* geometry, Curve, rotation minimizing frame}
|
|
* }
|
|
*
|
|
* @pre \p knots and \p frames have \p num entries.
|
|
* @param[in] spline
|
|
* The spline to query.
|
|
* @param[in] knots
|
|
* The knots to query \p spline at.
|
|
* @param[in] num
|
|
* Number of elements in \p knots and \p frames. Can be \c 0.
|
|
* @param[in] has_first_normal
|
|
* Indicates whether the normal of the first element of \p frames should
|
|
* be taken as starting value for the algorithm. If \c 0, the starting
|
|
* normal is determined based on the tangent of \p spline at \c knots[0].
|
|
* Note that, if the argument value is not \c 0, it is up to the caller of
|
|
* this function to ensure that the supplied normal is valid. The function
|
|
* only normalizes the supplied value.
|
|
* @param[in, out] frames
|
|
* Stores the computed frames.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If memory allocation failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_compute_rmf(const tsBSpline *spline,
|
|
const tsReal *knots,
|
|
size_t num,
|
|
int has_first_normal,
|
|
tsFrame *frames,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Generates a sequence of \p num knots with uniform distribution. \e Uniform
|
|
* means that consecutive knots have the same distance.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to query.
|
|
* @param[in] num
|
|
* Number of knots in \p knots.
|
|
* @param[out] knots
|
|
* Stores the generated knot sequence.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_bspline_uniform_knot_seq(const tsBSpline *spline,
|
|
size_t num,
|
|
tsReal *knots);
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name Transformation Functions
|
|
*
|
|
* A transformation modifies the internal state of a spline, e.g., its number
|
|
* of control points, the structure of its knot vector, its degree, and so
|
|
* on. It should be noted that some transformation modify a spline's state
|
|
* without changing its shape (e.g., ::ts_bspline_elevate). All transformations
|
|
* specify at least three parameters: i) an input spline (the spline to be
|
|
* transformed), ii) an output spline (the spline which receives the result of
|
|
* the transformation), and iii) a ::tsStatus (output parameter for error
|
|
* handling). Along with these parameters, additional parameters may be
|
|
* necessary to i) calculate a certain transformation (such as
|
|
* ::ts_bspline_tension) or ii) to store additional results (such as
|
|
* ::ts_bspline_insert_knot). Unless stated otherwise, the order of the
|
|
* parameters of a transformation \c t is:
|
|
*
|
|
* t(input, [additional_input], output, [additional_output], status)
|
|
*
|
|
* \b Note: None of the transformations releases the memory of the output
|
|
* spline before assigning the transformation result to it. Thus, when using
|
|
* the same output spline multiple times, make sure to release its memory
|
|
* before each call (after the first one). If not, severe memory leaks are to
|
|
* be expected:
|
|
*
|
|
* tsBSpline in = ... // an arbitrary spline
|
|
* tsBSpline out = ts_bspline_init(); // stores the result
|
|
*
|
|
* ts_bspline_to_beziers(&in, &out); // first transformation
|
|
* ... // some code
|
|
* ts_bspline_free(&out); // avoid memory leak.
|
|
* ts_bspline_tension(&in, 0.85, &out); // next transformation
|
|
*
|
|
* It is possible to pass a spline as input and output argument at the same
|
|
* time. In this case, the called transformation uses a temporary buffer to
|
|
* store the working data and result. If the transformation succeeds, the
|
|
* memory of the supplied spline is released and the transformation result is
|
|
* assigned to it. So even if a transformation fails, the internal state of the
|
|
* supplied splines stays intact (i.e., it remains unchanged).
|
|
*
|
|
* \b Note: It is not necessary to release the memory of a spline which is
|
|
* passed as input and output argument at the same time before calling the next
|
|
* transformation (in fact that would fail due to a null pointer):
|
|
*
|
|
* tsBSpline spline = ... // an arbitrary spline
|
|
* ts_bspline_to_beziers(&spline, &spline); // first transformation
|
|
* ts_bspline_tension(&spline, 0.85, &spline); // next transformation
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Returns the \p n'th derivative of \p spline as ::tsBSpline instance. The
|
|
* derivative of a spline \c s of degree \c d (\c d > 0) with \c m control
|
|
* points and \c n knots is another spline \c s' of degree \c d-1 with \c m-1
|
|
* control points and \c n-2 knots, defined over \c 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 \c 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 \c u is \c p
|
|
* rather than \c p+1. The derivative of a point (degree 0) is another point
|
|
* with coordinate 0. For more details, see:
|
|
*
|
|
* http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-derv.html
|
|
*
|
|
* If \p spline != \p deriv, the internal state of \p spline is not modified,
|
|
* that is, \p deriv is a new, independent ::tsBSpline instance.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to be derived.
|
|
* @param[in] n
|
|
* Number of derivations.
|
|
* @param[in] epsilon
|
|
* The maximum distance of discontinuous points. If negative,
|
|
* discontinuity is ignored and the derivative is computed based on the
|
|
* first result of the corresponding ::tsDeboorNet.
|
|
* @param[out] deriv
|
|
* The derivative of \p spline.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_UNDERIVABLE
|
|
* If \p spline is discontinuous at an internal knot and the distance
|
|
* between the corresponding points is greater than \p epsilon.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_derive(const tsBSpline *spline,
|
|
size_t n,
|
|
tsReal epsilon,
|
|
tsBSpline *deriv,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Inserts \p knot \p num times into the knot vector of \p spline. The
|
|
* operation fails if \p result would have an invalid knot vector (i.e.,
|
|
* multiplicity(knot) > order(result)). If \p spline != \p result, the internal
|
|
* state of \p spline is not modified, that is, \p result is a new, independent
|
|
* ::tsBSpline instance.
|
|
*
|
|
* @param[in] spline
|
|
* The spline into which \p knot is inserted \p num times.
|
|
* @param[in] knot
|
|
* The knot to be inserted.
|
|
* @param[in] num
|
|
* Number of insertions.
|
|
* @param[out] result
|
|
* The output spline.
|
|
* @param[out] k
|
|
* Stores the last index of \p knot in \p result.
|
|
* @param status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_U_UNDEFINED
|
|
* If \p knot is not within the domain of \p spline.
|
|
* @return TS_MULTIPLICITY
|
|
* If the multiplicity of \p knot in \p spline plus \p num is greater than
|
|
* the order of \p spline.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_insert_knot(const tsBSpline *spline,
|
|
tsReal knot, size_t num,
|
|
tsBSpline *result,
|
|
size_t *k,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Splits \p spline at \p knot. That is, \p knot is inserted into \p spline \c
|
|
* n times such that the multiplicity of \p knot is equal the spline's order.
|
|
* If \p spline != \p split, the internal state of \p spline is not modified,
|
|
* that is, \p split is a new, independent ::tsBSpline instance.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to be split.
|
|
* @param[in] knot
|
|
* The split point (knot).
|
|
* @param[out] split
|
|
* The split spline.
|
|
* @param[out] k
|
|
* Stores the last index of \p knot in \p split.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_U_UNDEFINED
|
|
* If \p spline is not defined at \p knot.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_split(const tsBSpline *spline,
|
|
tsReal knot,
|
|
tsBSpline *split,
|
|
size_t *k,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Straightens the control points of \p spline according to \p beta (0 yields a
|
|
* line connecting the first and the last control point; 1 keeps the original
|
|
* shape). The value of \p beta is clamped to [0, 1]. If \p spline != \p out,
|
|
* the internal state of \p spline is not modified, that is, \p out is a new,
|
|
* independent ::tsBSpline instance.
|
|
*
|
|
* This function is based on:
|
|
*
|
|
* @ARTICLE{10.1109/TVCG.2006.147,
|
|
* author = {Holten, Danny},
|
|
* journal = {IEEE Transactions on Visualization and Computer
|
|
* Graphics},
|
|
* title = {Hierarchical Edge Bundles: Visualization of
|
|
* Adjacency Relations in Hierarchical Data},
|
|
* year = {2006},
|
|
* volume = {12},
|
|
* number = {5},
|
|
* pages = {741-748},
|
|
* abstract = {A compound graph is a frequently encountered type of
|
|
* data set. Relations are given between items, and a
|
|
* hierarchy is defined on the items as well. We
|
|
* present a new method for visualizing such compound
|
|
* graphs. Our approach is based on visually bundling
|
|
* the adjacency edges, i.e., non-hierarchical edges,
|
|
* together. We realize this as follows. We assume that
|
|
* the hierarchy is shown via a standard tree
|
|
* visualization method. Next, we bend each adjacency
|
|
* edge, modeled as a B-spline curve, toward the
|
|
* polyline defined by the path via the inclusion edges
|
|
* from one node to another. This hierarchical bundling
|
|
* reduces visual clutter and also visualizes implicit
|
|
* adjacency edges between parent nodes that are the
|
|
* result of explicit adjacency edges between their
|
|
* respective child nodes. Furthermore, hierarchical
|
|
* edge bundling is a generic method which can be used
|
|
* in conjunction with existing tree visualization
|
|
* techniques. We illustrate our technique by providing
|
|
* example visualizations and discuss the results based
|
|
* on an informal evaluation provided by potential
|
|
* users of such visualizations.},
|
|
* keywords = {},
|
|
* doi = {10.1109/TVCG.2006.147},
|
|
* ISSN = {1941-0506},
|
|
* month = {Sep.},
|
|
* }
|
|
*
|
|
* Holten calls it "straightening" (page 744, equation 1).
|
|
*
|
|
* @param[in] spline
|
|
* The spline to be straightened.
|
|
* @param[in] beta
|
|
* The straightening factor. The value is clamped to the domain [0, 1].
|
|
* @param[out] out
|
|
* The straightened spline.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_tension(const tsBSpline *spline,
|
|
tsReal beta,
|
|
tsBSpline *out,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Decomposes \p spline into a sequence of Bezier curves by splitting it at
|
|
* each internal knot. If \p spline != \p beziers, the internal state of \p
|
|
* spline is not modified, that is, \p beziers is a new, independent
|
|
* ::tsBSpline instance.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to be decomposed.
|
|
* @param[out] beziers
|
|
* The bezier decomposition of \p spline.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_to_beziers(const tsBSpline *spline,
|
|
tsBSpline *beziers,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Elevates the degree of \p spline by \p amount and stores the result in
|
|
* \p elevated. If \p spline != \p elevated, the internal state of \p spline is
|
|
* not modified, that is, \p elevated is a new, independent ::tsBSpline
|
|
* instance.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to elevate.
|
|
* @param[in] amount
|
|
* How often to elevate the degree of \p spline.
|
|
* @param[in] epsilon
|
|
* In order to elevate the degree of a spline, it must be decomposed into
|
|
* a sequence of bezier curves (see ::ts_bspline_to_beziers). After degree
|
|
* elevation, the split points of the bezier curves are merged again. This
|
|
* parameter is used to distinguish between the split points of the
|
|
* decomposition process and the wanted discontinuity points. A viable
|
|
* default value is ::TS_POINT_EPSILON.
|
|
* @param[out] elevated
|
|
* The elevated spline.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If memory allocation failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_elevate_degree(const tsBSpline *spline,
|
|
size_t amount,
|
|
tsReal epsilon,
|
|
tsBSpline *elevated,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Modifies the splines \p s1 and \p s2 such that they have same degree and
|
|
* number of control points/knots (without modifying the shape of \p s1 and
|
|
* \p s2). The resulting splines are stored in \p s1_out and \p s2_out. If
|
|
* \p s1 != \p s1_out, the internal state of \p s1 is not modified, that is,
|
|
* \p s1_out is a new, independent ::tsBSpline instance. The same is true for
|
|
* \p s2 and \p s2_out.
|
|
*
|
|
* @param[in] s1
|
|
* The spline which is to be aligned with \p s2.
|
|
* @param[in] s2
|
|
* The spline which is to be aligned with \p s1.
|
|
* @param[in] epsilon
|
|
* Spline alignment relies on degree elevation. This parameter is used in
|
|
* ::ts_bspline_elevate_degree to check whether two control points, \c p1
|
|
* and \c p2, are "equal", that is, the distance between \c p1 and \c p2
|
|
* is less than or equal to \p epsilon. A viable default value is
|
|
* ::TS_POINT_EPSILON.
|
|
* @param[out] s1_out
|
|
* The aligned version of \p s1.
|
|
* @param[out] s2_out
|
|
* The aligned version of \p s2.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If memory allocation failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_align(const tsBSpline *s1,
|
|
const tsBSpline *s2,
|
|
tsReal epsilon,
|
|
tsBSpline *s1_out,
|
|
tsBSpline *s2_out,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Interpolates between \p origin and \p target with respect to the time
|
|
* parameter \p t (domain: [0, 1]; clamped if necessary). The resulting spline
|
|
* is stored in \p out. Because it is to be expected that this function is
|
|
* called several times in a row (e.g., to have a smooth transition from one
|
|
* spline to another), memory for \p out is allocated only if it points to NULL
|
|
* or if it has to be enlarged to store the result of the interpolation (which
|
|
* can only happen if \p origin or \p target---or both---have been changed
|
|
* since the last call). This way, this function can be used as follows:
|
|
*
|
|
* tsReal t;
|
|
* tsBSpline origin = ...
|
|
* tsBSpline target = ...
|
|
* tsBSpline morph = ts_bspline_init();
|
|
* for (t = (tsReal) 0.0; t <= (tsReal) 1.0; t += (tsReal) 0.001)
|
|
* ts_bspline_morph(&origin, &target, t, ..., &morph, ...);
|
|
* ts_bspline_free(&morph);
|
|
*
|
|
* It should be noted that this function, if necessary, aligns \p origin and \p
|
|
* target using ::ts_bspline_align. In order to avoid the overhead of spline
|
|
* alignment, \p origin and \p target should be aligned in advance.
|
|
*
|
|
* @param[in] origin
|
|
* Origin spline.
|
|
* @param[in] target
|
|
* Target spline.
|
|
* @param[in] t
|
|
* The time parameter. If 0, \p out becomes \p origin. If 1, \p out becomes
|
|
* \p target. Note that the value passed is clamped to the domain [0, 1].
|
|
* @param[in] epsilon
|
|
* If \p origin and \p target must be aligned, this parameter is passed
|
|
* ::ts_bspline_elevate_degree to check whether two control points, \c p1
|
|
* and \c p2, are "equal", that is, the distance between \c p1 and \c p2
|
|
* is less than or equal to \p epsilon. A viable default value is
|
|
* ::TS_POINT_EPSILON.
|
|
* @param[out] out
|
|
* The resulting spline.
|
|
* @return[out] TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If memory allocation failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_morph(const tsBSpline *origin,
|
|
const tsBSpline *target,
|
|
tsReal t,
|
|
tsReal epsilon,
|
|
tsBSpline *out,
|
|
tsStatus *status);
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name Serialization and Persistence
|
|
*
|
|
* The following functions can be used to serialize and persist (i.e., store
|
|
* the serialized data in a file) splines. There are also functions to load
|
|
* serialized splines.
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Serializes \p spline to a null-terminated JSON string and stores the result
|
|
* in \p json.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to be serialized.
|
|
* @param[out] json
|
|
* The serialized JSON string.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_to_json(const tsBSpline *spline,
|
|
char **json,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Parses \p json and stores the result in \p spline.
|
|
*
|
|
* @param[in] json
|
|
* The JSON string to be parsed.
|
|
* @param[out] spline
|
|
* The output spline.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_PARSE_ERROR
|
|
* If an error occurred while parsing \p json.
|
|
* @return TS_DIM_ZERO
|
|
* If the dimension is \c 0.
|
|
* @return TS_LCTRLP_DIM_MISMATCH
|
|
* If the length of the control point array modulo dimension is not \c 0.
|
|
* @return TS_DEG_GE_NCTRLP
|
|
* If the degree is greater or equals to the number of control points.
|
|
* @return TS_NUM_KNOTS
|
|
* If the number of knots does not match to the number of control points
|
|
* plus the degree of the spline.
|
|
* @return TS_KNOTS_DECR
|
|
* If the knot vector is decreasing.
|
|
* @return TS_MULTIPLICITY
|
|
* If there is a knot with multiplicity greater than order.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_parse_json(const char *json,
|
|
tsBSpline *spline,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Saves \p spline as JSON ASCII file.
|
|
*
|
|
* @param[in] spline
|
|
* The spline to be saved.
|
|
* @param[in] path
|
|
* Path of the JSON file.
|
|
* @param[out] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_IO_ERROR
|
|
* If an error occurred while saving \p spline.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_save(const tsBSpline *spline,
|
|
const char *path,
|
|
tsStatus *status);
|
|
|
|
/**
|
|
* Loads \p spline from a JSON ASCII file.
|
|
*
|
|
* @param[in] path
|
|
* Path of the JSON file to be loaded.
|
|
* @param[out] spline
|
|
* The output spline.
|
|
* @param[ou] status
|
|
* The status of this function. May be NULL.
|
|
* @return TS_SUCCESS
|
|
* On success.
|
|
* @return TS_IO_ERROR
|
|
* If \p path does not exist.
|
|
* @return TS_PARSE_ERROR
|
|
* If an error occurred while parsing the contents of \p path.
|
|
* @return TS_DIM_ZERO
|
|
* If the dimension is \c 0.
|
|
* @return TS_LCTRLP_DIM_MISMATCH
|
|
* If the length of the control point array modulo dimension is not \c 0.
|
|
* @return TS_DEG_GE_NCTRLP
|
|
* If the degree is greater or equals to the number of control points.
|
|
* @return TS_NUM_KNOTS
|
|
* If the number of knots does not match to the number of control points
|
|
* plus the degree of the spline.
|
|
* @return TS_KNOTS_DECR
|
|
* If the knot vector is decreasing.
|
|
* @return TS_MULTIPLICITY
|
|
* If there is a knot with multiplicity greater than order.
|
|
* @return TS_MALLOC
|
|
* If allocating memory failed.
|
|
*/
|
|
tsError TINYSPLINE_API
|
|
ts_bspline_load(const char *path,
|
|
tsBSpline *spline,
|
|
tsStatus *status);
|
|
|
|
|
|
|
|
/*! @name Vector Math
|
|
*
|
|
* Vector math is a not insignificant part of TinySpline, and so it's not
|
|
* surprising that some utility functions around vectors are needed. Because
|
|
* these functions might be useful for others, they are part of TinySpline's
|
|
* public API. However, note that the code is \b not highly optimized (with,
|
|
* for example, instruction set extensions like SSE). If high performance
|
|
* vector math is needed, other libraries should be used instead.
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Initializes vector \p out with \p x and \p y.
|
|
*
|
|
* @pre
|
|
* \p out has dimensionality \c 2.
|
|
* @param[out] out
|
|
* Target vector.
|
|
* @param[in] x
|
|
* The x value.
|
|
* @param[in] y
|
|
* The y value.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec2_init(tsReal *out,
|
|
tsReal x,
|
|
tsReal y);
|
|
|
|
/**
|
|
* Initializes vector \p out with \p x, \p y, and \p z.
|
|
*
|
|
* @pre
|
|
* \p out has dimensionality \c 3.
|
|
* @param[out] out
|
|
* Target vector.
|
|
* @param[in] x
|
|
* The x value.
|
|
* @param[in] y
|
|
* The y value.
|
|
* @param[in] z
|
|
* The z value.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec3_init(tsReal *out,
|
|
tsReal x,
|
|
tsReal y,
|
|
tsReal z);
|
|
|
|
/**
|
|
* Initializes vector \p out with \p x, \p y, \p z, and \p w.
|
|
*
|
|
* @pre
|
|
* \p out has dimensionality \c 4.
|
|
* @param[out] out
|
|
* Target vector.
|
|
* @param[in] x
|
|
* The x value.
|
|
* @param[in] y
|
|
* The y value.
|
|
* @param[in] z
|
|
* The z value.
|
|
* @param[in] w
|
|
* The w value.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec4_init(tsReal *out,
|
|
tsReal x,
|
|
tsReal y,
|
|
tsReal z,
|
|
tsReal w);
|
|
|
|
/**
|
|
* Copies the values of vector \p x (a vector with dimensionality \p dim) to
|
|
* vector \p out (a vector with dimensionality \c 2). If \p dim is less than \c
|
|
* 2, the remaining values of \p out are set to \c 0. Excess values in \p x
|
|
* (i.e., \p dim is greater than \c 2) are ignored.
|
|
*
|
|
* @pre
|
|
* \p out has dimensionality \c 2.
|
|
* @param[out] out
|
|
* Target vector.
|
|
* @param[in] x
|
|
* Vector to read the values from.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec2_set(tsReal *out,
|
|
const tsReal *x,
|
|
size_t dim);
|
|
|
|
/**
|
|
* Copies the values of vector \p x (a vector with dimensionality \p dim) to
|
|
* vector \p out (a vector with dimensionality \c 3). If \p dim is less than \c
|
|
* 3, the remaining values of \p out are set to \c 0. Excess values in \p x
|
|
* (i.e., \p dim is greater than \c 3) are ignored.
|
|
*
|
|
* @pre
|
|
* \p out has dimensionality \c 3.
|
|
* @param[out] out
|
|
* Target vector.
|
|
* @param[in] x
|
|
* Vector to read the values from.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec3_set(tsReal *out,
|
|
const tsReal *x,
|
|
size_t dim);
|
|
|
|
/**
|
|
* Copies the values of vector \p x (a vector with dimensionality \p dim) to
|
|
* vector \p out (a vector with dimensionality \c 4). If \p dim is less than \c
|
|
* 4, the remaining values of \p out are set to \c 0. Excess values in \p x
|
|
* (i.e., \p dim is greater than \c 4) are ignored.
|
|
*
|
|
* @pre
|
|
* \p out has dimensionality \c 4.
|
|
* @param[out] out
|
|
* Target vector.
|
|
* @param[in] x
|
|
* Vector to read the values from.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec4_set(tsReal *out,
|
|
const tsReal *x,
|
|
size_t dim);
|
|
|
|
/**
|
|
* Adds vector \p y to vector \p x and stores the result in vector \p out.
|
|
*
|
|
* @param[in] x
|
|
* First vector.
|
|
* @param[in] y
|
|
* Second vector.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x, \p y, and \p out.
|
|
* @param[out] out
|
|
* Result vector. Can be same as \p x or \p y, i.e., the result can be
|
|
* stored in-place.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec_add(const tsReal *x,
|
|
const tsReal *y,
|
|
size_t dim,
|
|
tsReal *out);
|
|
|
|
/**
|
|
* Subtracts vector \p y from vector \p x and stores the result in vector \p
|
|
* out.
|
|
*
|
|
* @param[in] x
|
|
* First vector.
|
|
* @param[in] y
|
|
* Second vector.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x, \p y, and \p out.
|
|
* @param[out] out
|
|
* Result vector. Can be same as \p x or \p y, i.e., the result can be
|
|
* stored in-place.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec_sub(const tsReal *x,
|
|
const tsReal *y,
|
|
size_t dim,
|
|
tsReal *out);
|
|
|
|
/**
|
|
* Computes the dot product (also known as scalar product) of the vectors \p x
|
|
* and \p y.
|
|
*
|
|
* @post
|
|
* \c 0 if \p dim is \c 0.
|
|
* @param[in] x
|
|
* First vector.
|
|
* @param[in] y
|
|
* Second vector.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x and \p y.
|
|
* @return
|
|
* The dot product of \p x and \y.
|
|
*/
|
|
tsReal TINYSPLINE_API
|
|
ts_vec_dot(const tsReal *x,
|
|
const tsReal *y,
|
|
size_t dim);
|
|
|
|
/**
|
|
* Computes the angle in degrees between the vectors \p x and \p y. The angle
|
|
* returned is unsigned, that is, the smaller of the two possible angles is
|
|
* computed. The nullable parameter \p buf servers as a buffer in case \p x or
|
|
* \p y (or both) are not normalized. If \p buf is \c NULL, it is expected that
|
|
* \p x and \p y are already normalized. If \p buf is not \c NULL, a storage
|
|
* twice the size of \p dim is expected in which the normalized vectors of \p x
|
|
* and \p y are stored.
|
|
*
|
|
* @pre
|
|
* \p buf is either \c NULL or has length <tt>2 * dim</tt>.
|
|
* @param[in] x
|
|
* First vector.
|
|
* @param[in] y
|
|
* Second vector.
|
|
* @param[out] buf
|
|
* A buffer in which the normalized vectors of \p x and \y are stored. If
|
|
* \c NULL, it is expected that \p x and \p y are already normalized.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x and \p y.
|
|
* @return
|
|
* The angle between \p x and \y with <tt>0.0 <= angle <= 180.0</tt>.
|
|
*/
|
|
tsReal TINYSPLINE_API
|
|
ts_vec_angle(const tsReal *x,
|
|
const tsReal *y,
|
|
tsReal *buf,
|
|
size_t dim);
|
|
|
|
/**
|
|
* Computes the cross product (also known as vector product or directed area
|
|
* product) of the vectors \p x and \p y.
|
|
*
|
|
* @pre \p x and \p y have dimensionality \c 3.
|
|
* @param[in] x
|
|
* First vector.
|
|
* @param[in] y
|
|
* Second vector.
|
|
* @param[out] out
|
|
* Result vector. Can be same as \p x or \p y, i.e., the result can be
|
|
* stored in-place.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec3_cross(const tsReal *x,
|
|
const tsReal *y,
|
|
tsReal *out);
|
|
|
|
/**
|
|
* Normalizes vector \p x.
|
|
*
|
|
* @post
|
|
* \c 0 if the length of \p x (see ::ts_vec_mag) is less than
|
|
* ::TS_LENGTH_ZERO.
|
|
* @param[in] x
|
|
* A vector.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x.
|
|
* @param[out] out
|
|
* Result vector. Can be same as \p x, i.e., the result can be stored
|
|
* in-place.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec_norm(const tsReal *x,
|
|
size_t dim,
|
|
tsReal *out);
|
|
|
|
/**
|
|
* Determines the length of vector \p x.
|
|
*
|
|
* @post
|
|
* \c 0 if \p dim is \c 0.
|
|
* @param[in] x
|
|
* A vector.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x.
|
|
*/
|
|
tsReal TINYSPLINE_API
|
|
ts_vec_mag(const tsReal *x,
|
|
size_t dim);
|
|
|
|
/**
|
|
* Multiplies vector \p x with scalar \p val and stores the result in vector \p
|
|
* out.
|
|
*
|
|
* @param[in] x
|
|
* A vector.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x.
|
|
* @param[in] val
|
|
* Scalar value.
|
|
* @param[out] out
|
|
* Result vector. Can be same as \p x, i.e., the result can be stored
|
|
* in-place.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_vec_mul(const tsReal *x,
|
|
size_t dim,
|
|
tsReal val,
|
|
tsReal *out);
|
|
/*! @} */
|
|
|
|
|
|
|
|
/*! @name Utility Functions
|
|
*
|
|
* @{
|
|
*/
|
|
/**
|
|
* Returns whether the knots \p x and \p y are equal with respect to the epsilon
|
|
* environment ::TS_KNOT_EPSILON (i.e., their distance is <u>less</u> than
|
|
* ::TS_KNOT_EPSILON).
|
|
*
|
|
* @param[in] x
|
|
* First knot.
|
|
* @param[in] y
|
|
* Second knot.
|
|
* @return 1
|
|
* If \p x and \p y are equal.
|
|
* @return 0
|
|
* If \p x and \p y are not equal.
|
|
*/
|
|
int TINYSPLINE_API
|
|
ts_knots_equal(tsReal x,
|
|
tsReal y);
|
|
|
|
/**
|
|
* Fills the given array \p arr with \p val.
|
|
*
|
|
* @param[in] arr
|
|
* The array to be filled.
|
|
* @param[in] num
|
|
* Fill length.
|
|
* @param[in] val
|
|
* The value to fill into \p arr.
|
|
*/
|
|
void TINYSPLINE_API
|
|
ts_arr_fill(tsReal *arr,
|
|
size_t num,
|
|
tsReal val);
|
|
|
|
/**
|
|
* Returns the euclidean distance of the points \p x and \p y.
|
|
*
|
|
* @param[in] x
|
|
* First point.
|
|
* @param[in] y
|
|
* Second point.
|
|
* @param[in] dim
|
|
* Dimensionality of \p x and \p y.
|
|
* @return
|
|
* The euclidean distance of the points \p x and \p y.
|
|
*/
|
|
tsReal TINYSPLINE_API
|
|
ts_distance(const tsReal *x,
|
|
const tsReal *y,
|
|
size_t dim);
|
|
/*! @} */
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* TINYSPLINE_H */
|
|
|