////////////////////////////////////////////////////////////////////////////////
/** 
  \file
  \brief \ru Двумерная NURBS кривая.
         \en Two-dimensional NURBS curve. \~

*/
////////////////////////////////////////////////////////////////////////////////

#ifndef __CUR_NURBS_H
#define __CUR_NURBS_H


#include <templ_c_array.h>
#include <templ_array2.h>
#include <tool_multithreading.h>
#include <mb_homogeneous.h>
#include <mb_nurbs_function.h>
#include <mb_point_mating.h>
#include <cur_polycurve.h>
#include <alg_base.h>
#include <vector>
#include <iterator>


class  MATH_CLASS MbLine;
class  MATH_CLASS MbBezier;
class  MATH_CLASS MbContour;


//------------------------------------------------------------------------------
/** \brief \ru NURBS кривая в двумерном пространстве.
           \en NURBS curve in two-dimensional space. \~
  \details \ru NURBS кривая определяется контрольными точками pointList, 
    весами контрольных точек weights, узловым вектором knots и порядком сплайна degree.\n
    Аббревиатура NURBS получена из первых букв словосочетания Non-Uniform Rational B-Spline.
    NURBS кривая не проходит через свои контрольные точки.
    Узловой вектор knots должен представлять собой неубывающую последовательность действительных чисел.
    Множества pointList и weights должны содержать одинаковое количество элементов. 
    Для не замкнутой кривой узловой вектор knots должен содержать количество элементов множества pointList плюс degree.
    Для замкнутой кривой кривой узловой вектор knots должен содержать количество элементов множества pointList плюс 2*degree-1. 
    Минимальное значение параметра сплайна равно значению элемента узлового вектора с индексом degree-1. 
    Максимальное значение параметра сплайна равно значению элемента узлового вектора с индексом, равным последнему элементу минус degree-1. 
    Расчет кривой в каждой своей точке производится на основе нормированных неоднородных В-сплайнов.\n
    Семейство В-сплайнов определяется заданной неубывающей последовательностью узловых параметров и заданным порядком B-сплайна.\n
           \en NURBS curve is defined by 'pointList' control points, 
    weights of control points ('weights'), knot vector ('knots') and order of spline ('degree').\n
    Abbreviation of NURBS is obtained from the first letters of the Non-Uniform Rational B-Spline phrase.
    NURBS curve doesn't pass through its control points.
    'knots' knot vector has to be not decreasing sequence of real numbers.
    'pointList' and 'weights' sets have to contain the same count of elements. 
    For not closed curve 'knots' knot vector has to contain the count of elements of 'pointList' set plus 'degree'.
    For closed curve 'knots' knot vector has to contain the count of elements of 'pointList' set plus 2*degree-1. 
    Minimal value of spline parameter is equal to value of element of knot vector with degree-1 index. 
    Maximal value of spline parameter is equal to value of element of knot vector with index, which is equal to index of last element minus degree-1. 
    Curve calculation at each point is performed by normalized non-uniform B-splines.\n
    Family of B-splines is defined by given crescent sequence of knot parameters and given order of B-spline.\n \~
  \ingroup Curves_2D
*/
// ---

class MATH_CLASS MbNurbs : public MbPolyCurve {
private:
  size_t            degree;        ///< \ru Порядок В-сплайна (порядок = степень + 1). \en Order of B-spline (order = degree + 1). 
  ptrdiff_t         uppKnotsIndex; ///< \ru Последний индекс узлового вектора. \en Last index of knot vector. 
  SArray<double>    knots;         ///< \ru Узловой вектор. \en Knot vector. 
  SArray<double>    weights;       ///< \ru Множество весов контрольных точек. \en Set of weights of the control points. 
  MbeNurbsCurveForm form;          ///< \ru Форма кривой. \en Form of curve. 

private:
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
  /** \brief \ru Вспомогательные данные. 
             \en Auxiliary data. \~
      \details \ru Вспомогательные данные служат для ускорения работы объекта.
               \en Auxiliary data are used for fast calculations. \n \~
    */
  // ---
  class MbNurbsAuxiliaryData : public AuxiliaryData {
  public:
    double            tCalc; ///< \ru Текущее значение параметра, по которому вычислены временные данные кривой. \en Current value of parameter the curve temporary data are calculated by. 
    double          * nd;    ///< \ru B - базис. \en B - basis. 
    MbHomogeneous   * h0;    ///< \ru Текущий сегмент. \en Current segment. 
    MbHomogeneous   * h1;    ///< \ru Текущий сегмент. \en Current segment. 
    MbHomogeneous   * h2;    ///< \ru Текущий сегмент. \en Current segment. 
    MbHomogeneous   * h3;    ///< \ru Текущий сегмент. \en Current segment. 
    double          * wc;               ///< \ru Насчитанные значения весов (может быть ноль). \en Calculated values of points (can be null). 
    MbVector          rc[cdt_CountDer]; ///< \ru Насчитанные значения точек. \en Calculated values of points. 
    ptrdiff_t         leftIndex;        ///< \ru Левый индекс узлового вектора. \en Left index of knot vector. 
    double          * m_left;
    double          * m_right;
  public:
    MbNurbsAuxiliaryData();
    MbNurbsAuxiliaryData( const MbNurbsAuxiliaryData & init );
    virtual ~MbNurbsAuxiliaryData();
    void FreeMemory();
    bool CatchMemory(  ptrdiff_t, bool );
  }; // MbNurbsAuxiliaryData 

  mutable CacheManager<MbNurbsAuxiliaryData> cache;

public://protected:
  DEPRECATE_DECLARE MbNurbs(); ///< \deprecated \ru Метод устарел. \en The method is deprecated.
protected:
  /** \brief \ru Конструктор.
             \en Constructor. \~
      \details \ru Конструктор по порядку, точкам, параметрам и признаку замкнутости. При недопустимых параметрах initDegree и points поведение кривой не определено.\n
               \en Constructor by order, points, parameters and an attribute of closedness. If parameters initDegree and points is invalid then the curve behavior is undefined. \n \~
    \param[in] degree  - \ru Порядок сплайна. Должен быть больше единицы. Не должен превышать количество контрольных точек.
                         \en A spline order. It must be greater than unity. It shouldn't exceed count of control points. \~
    \param[in] cls     - \ru Признак замкнутости.
                         \en A closedness attribute. \~
    \param[in] points  - \ru Набор контрольных точек. Количество точек должно быть больше или равно двум.
                         \en Set of control points. Count of points must be greater than or equal to two. \~
    \param[in] weights - \ru Набор весов для контрольных точек. Количество весов должно соответствовать количеству точек.
                         \en Set of weights for control points. Count of weights must be equal to count of points. \~
    \param[in] knots   - \ru Последовательность узловых параметров.
                         \en Sequence of knot parameters. \~ 
  */
  template <class PointsVector, class DoubleVector>
  MbNurbs( size_t degree, bool cls, const PointsVector & points,
           const DoubleVector * weights, const DoubleVector * knots );
  MbNurbs( const MbNurbs & );
public :
  virtual ~MbNurbs();

private:
  static  MbNurbs * Create();
public:
  /** \brief \ru Создать копию сплайна.
             \en Create copy of spline. \~
      \details \ru Создать копию сплайна.\n
               \en Create copy of spline.\n \~
      \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи.
              \en Returns pointer to the created object or null pointer in case of failure. \~
  */
  static  MbNurbs * Create( const MbNurbs & );
  /** \brief \ru Создать сплайн.
             \en Create spline. \~
      \details \ru Создать сплайн и установить параметры сплайна.\n
               \en Create spline and set parameters of spline.\n \~
      \param[in] initDegree  - \ru Порядок сплайна.
                               \en A spline order. \~ 
      \param[in] initPoints  - \ru Набор контрольных точек.
                               \en Set of control points. \~
      \param[in] initClosed  - \ru Признак замкнутости.
                               \en A closedness attribute. \~
      \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи.
              \en Returns pointer to the created object or null pointer in case of failure. \~
  */
  template <class PointsVector>
  static  MbNurbs *   Create( ptrdiff_t initDegree, const PointsVector & initPoints, bool initClosed )
  {
    MbNurbs * resNurbs = Create();
    if ( !resNurbs->Init( initDegree, initPoints, initClosed ) )
      ::DeleteItem( resNurbs );
    return resNurbs;
  }
  /** \brief \ru Создать сплайн.
             \en Create spline. \~
      \details \ru Создать сплайн и установить параметры сплайна.\n
               \en Create spline and set parameters of spline.\n \~
      \param[in] initDegree  - \ru Порядок сплайна.
                               \en A spline order. \~ 
      \param[in] initPoints  - \ru Набор контрольных точек.
                               \en Set of control points. \~
      \param[in] initClosed  - \ru Признак замкнутости.
                               \en A closedness attribute. \~
      \param[in] initWeights - \ru Набор весов для контрольных точек.
                               \en Set of weights for control points. \~ 
      \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи.
              \en Returns pointer to the created object or null pointer in case of failure. \~
  */
  template <class PointsVector, class DoubleVector>
  static  MbNurbs *   Create( ptrdiff_t initDegree, const PointsVector & initPoints, bool initClosed,
                              const DoubleVector * initWeights )
  {
    MbNurbs * resNurbs = Create();
    if ( !resNurbs->Init( initDegree, initPoints, initClosed, initWeights ) )
      ::DeleteItem( resNurbs );
    return resNurbs;
  }
  /** \brief \ru Создать сплайн.
             \en Create spline. \~
    \details \ru Создать сплайн и установить параметры сплайна.\n
             \en Create spline and set parameters of spline.\n \~
    \param[in] initDegree  - \ru Порядок сплайна.
                             \en A spline order. \~ 
    \param[in] initClosed  - \ru Признак замкнутости.
                              \en A closedness attribute. \~
    \param[in] initPoints  - \ru Набор контрольных точек.
                             \en Set of control points. \~ 
    \param[in] initWeights - \ru Набор весов для контрольных точек.
                             \en Set of weights for control points. \~
    \param[in] initKnots   - \ru Неубывающая последовательность весов.
                             \en Non-decreasing sequence of weights. \~
    \param[in] initForm    - \ru Тип построения.
                             \en Type of construction. \~
    \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи.
            \en Returns pointer to the created object or null pointer in case of failure. \~
  */
  template <class PointsVector, class DoubleVector>
  static  MbNurbs *   Create( ptrdiff_t initDegree, bool initClosed, const PointsVector & initPoints, 
                              const DoubleVector & initWeights, const DoubleVector & initKnots, 
                              MbeNurbsCurveForm initForm = ncf_Unspecified )
  {
    MbNurbs * resNurbs = Create();
    if ( !resNurbs->Init( initDegree, initClosed, initPoints, initWeights, initKnots, initForm ) )
      ::DeleteItem( resNurbs );
    return resNurbs;
  }

  /** \brief \ru Создать сплайн.
             \en Create spline. \~
      \details \ru Сплайн, проходящий через заданные точки при заданных параметрах.\n
        В случае замкнутости нужно передавать массив с совпадением первой и последней точек.\n
               \en Spline passing through given points at given parameters.\n
        In case of closedness it is necessary to pass the array with coincidence of the first and the last points.\n \~
      \param[in] degree  - \ru Порядок сплайна.
                           \en A spline order. \~
      \param[in] cls     - \ru Признак замкнутости.
                           \en A closedness attribute. \~
      \param[in] points  - \ru Набор точек, через которые проходит сплайн.
                           \en Set of points which the spline passes through. \~
      \param[in] params  - \ru Последовательность узловых параметров.
                           \en Sequence of knot parameters. \~
      \param[in] aKnots  - \ru Неубывающая последовательность узлов.
                           \en Nondecreasing sequence of knots. \~
      \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи.
              \en Returns pointer to the created object or null pointer in case of failure. \~
  */
  static  MbNurbs *   CreateThrough( size_t degree, bool cls, const SArray<MbCartPoint> & points,
                                     const SArray<double> & params, SArray<double> * aKnots = nullptr );
  /** \brief \ru Заполнить NURBS по данным parasolid.
              \en Fill NURBS by parasolid data. \~
      \details \ru Заполнить NURBS по данным parasolid.\n
                \en Fill NURBS by parasolid data.\n \~
      \param[in] degree     - \ru Степень сплайна.
                              \en Order of spline. \~
      \param[in] closed     - \ru Признак замкнутости.
                              \en A closedness attribute. \~
      \param[in] rational   - \ru Является ли кривая рациональной. Если true - строится NURBS, false - кривая Безье.
                              \en Whether the curve is rational. if true then NURBS is created, false - Bezier curve. \~
      \param[in] count      - \ru Количество контрольных точек.
                              \en Count of control points. \~
      \param[in] verts      - \ru Массив координат точек. Если сплайн рациональный, четвертая координата - вес точки.
                              \en An array of coordinates of points. If spline is rational, then the fourth coordinate - weight of point. \~ 
      \param[in] vertsCount - \ru Количество элементов в массиве verts.
                              \en Count of elements in 'verts' array. \~
      \param[in] mul        - \ru Массив с данными о кратности каждого узла.
                              \en Array with multiplicity of each knot. \~
      \param[in] mulCount   - \ru Количество элементов в массиве mul.
                              \en Count of elements in 'mul' array. \~
      \param[in] knots       - \ru Массив со значениями параметров в узлах. Каждое значение представлено один раз. 
                               Информация о кратности узла лежит в элементе массива mul с тем же номером.
                               \en Array with values of parameters at knots. Each value is presented only once. 
                               Information about knot multiplicity is in the element of 'mul' array with the same index. \~
      \param[in] knotsCount  - \ru Количество элементов в массиве knots.
                               \en Count of elements in 'knots' array. \~
      \param[in] scl         - \ru Коэффициент масштабирования.
                               \en A scale factor. \~
      \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи.
              \en Returns pointer to the created object or null pointer in case of failure. \~
  */
  static  MbNurbs *   CreateParasolid( size_t degree, bool closed, bool rational, ptrdiff_t count,
                                       const CcArray<double> & verts, ptrdiff_t vertsCount,
                                       const CcArray<ptrdiff_t> & mul, ptrdiff_t mulCount,
                                       const CcArray<double> & knots, ptrdiff_t knotsCount,
                                       double scl );
  /** \brief \ru Создать сплайн.
             \en Create spline. \~
      \details \ru Создать сплайн и установить параметры сплайна.\n
               \en Create spline and set parameters of spline. \n \~
      \param[in] p1  - \ru Начальная точка, через которую проходит сплайн.
                       \en The initial point the spline passes through. \~
      \param[in] v1  - \ru Касательный вектор к кривой в начальной точке.
                       \en A tangent vector to the curve at the start point. \~
      \param[in] p2  - \ru Конечная точка, через которую проходит сплайн.
                       \en The final point the spline passes through. \~
      \param[in] v2  - \ru Касательный вектор к кривой в конечной точке.
                       \en A tangent vector to the curve at the end point. \~
      \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи.
              \en Returns pointer to the created object or null pointer in case of failure. \~
  */
  static  MbNurbs *   CreateCube( const MbCartPoint & p1, const MbVector & v1, const MbCartPoint & p2, const MbVector & v2 );
  /** \brief \ru Создать сплайн.
             \en Create spline. \~
    \details \ru Конструктор сплайна, описывающего дугу конического сечения.\n
             \en Constructor of a spline describing the arc of a conic section. \n \~
    \param[in] points  - \ru Набор из четырех точек, через которое проходит сечение.
                         Первая и последняя точки определяют начало и конец дуги, соответственно.
                         \en A set of four points the section passes through.
                         The first and last points define the beginning and ending of the arc respectively. \~
    \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи.
            \en Returns pointer to the created object or null pointer in case of failure. \~
  */
  static  MbNurbs *   CreateArc( const SArray<MbCartPoint> & points );
  /** \brief \ru Создать сплайн.
             \en Create spline. \~
    \details \ru Конструктор сплайна, описывающего дугу окружности.\n
             \en Constructor of a spline describing the arc of a circle. \n \~
    \param[in] a2 - \ru Половина угла раствора.
                    \en A half opening angle. \~
    \param[in] p1 - \ru Начальная точка дуги.
                    \en The starting point of the arc. \~
    \param[in] p2 - \ru Конечная точка дуги.
                    \en The end point of the arc. \~
    \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи.
            \en Returns pointer to the created object or null pointer in case of failure. \~
  */
  static  MbNurbs *   CreateArc( double a2, const MbCartPoint & p1, const MbCartPoint & p2 );
  /** \brief \ru Создать сплайн.
             \en Create spline. \~
    \details \ru Конструктор сплайна, описывающего волнистую линию.\n
             \en Constructor of a spline describing a wavy line. \n \~
    \param[in] p1      - \ru Начальная точка кривой.
                         \en Start point of curve. \~
    \param[in] p2      - \ru Конечная точка кривой.
                         \en End point of curve. \~
    \param[in] height  - \ru Высота гребешка волны.
                         \en Height of the wave. \~
    \param[in] periode - \ru Период волны.
                         \en Period of the wave. \~
    \return \ru Возвращает указатель на созданный объект или нулевой указатель в случае неудачи.
            \en Returns pointer to the created object or null pointer in case of failure. \~
  */
  static  MbNurbs *   CreateWavyLine( const MbCartPoint & p1, const MbCartPoint & p2, double height, double periode );

public :
  VISITING_CLASS( MbNurbs ); 

  /** \ru \name Функции инициализации NURBS-кривой.
      \en \name Functions of NURBS curve initialization.
      \{ */
  // \ru Приведенные ниже функции меняют степень degree, поэтому в них необходим вызов CatchMemory(); \en The following functions change the degree "degree" therefore they call CatchMemory(); 

  /// \ru Установить параметры сплайна по заданной NURBS-кривой. \en Set the spline parameters by a given NURBS curve. 
  void        Init( const MbNurbs & );
  /** \brief \ru Инициализация.
              \en Initialization. \~
      \details \ru Установить параметры сплайна.\n
                \en Set parameters of spline.\n \~
      \param[in] initDegree  - \ru Порядок сплайна.
                                \en A spline order. \~ 
      \param[in] initPoints  - \ru Набор контрольных точек.
                                \en Set of control points. \~
      \param[in] initClosed  - \ru Признак замкнутости.
                                \en A closedness attribute. \~
  */

  template <class PointsVector>
  bool        Init( size_t initDegree, const PointsVector & initPoints, bool initClosed )
  {
    if ( ::IsValidNurbsParams( initDegree, initClosed, initPoints.size() ) )
    {
      Refresh(); // Must come first, since frees allocated memory

      degree    = initDegree;
      form      = ncf_Unspecified;
      closed    = initClosed;
      uppIndex  = (ptrdiff_t)initPoints.size() - 1;
      pointList.assign( initPoints.begin(), initPoints.end() );
      weights.assign( initPoints.size(), 1.0 );

      DefineKnotsVector();
      return true;
    }

    return false;
  }

  /** \brief \ru Инициализация.
              \en Initialization. \~
      \details \ru Установить параметры сплайна.\n
                \en Set parameters of spline.\n \~
      \param[in] initDegree  - \ru Порядок сплайна.
                                \en A spline order. \~ 
      \param[in] initPoints  - \ru Набор контрольных точек.
                                \en Set of control points. \~
      \param[in] initClosed  - \ru Признак замкнутости.
                                \en A closedness attribute. \~
      \param[in] initWeights - \ru Набор весов для контрольных точек.
                                \en Set of weights for control points. \~ 
  */
  template <class PointsVector, class DoubleVector>
  bool        Init( size_t initDegree, const PointsVector & initPoints, bool initClosed,
                    const DoubleVector * initWeights )
  {
    if ( ::IsValidNurbsParamsExt( initDegree, initClosed, initPoints, initWeights ) )
    {
      Refresh(); // Must come first, since frees allocated memory

      degree    = initDegree;
      form      = ncf_Unspecified;
      closed    = initClosed;
      uppIndex  = (ptrdiff_t)initPoints.size() - 1;
      pointList.assign( initPoints.begin(), initPoints.end() );

      if ( initWeights != nullptr ) {
        if ( (ptrdiff_t)initWeights->size() == uppIndex + 1 )
          weights.assign( initWeights->begin(), initWeights->end() );
        else {
          C3D_ASSERT_UNCONDITIONAL( false ); // Wrong size of weights vector
          weights.assign( initPoints.size(), 1.0 );
        }
      }
      else {
        weights.assign( initPoints.size(), 1.0 );
      }

      DefineKnotsVector();
      return true;
    }

    return false;
  }

  /** \brief \ru Инициализация.
              \en Initialization. \~
      \details \ru Установить параметры сплайна.\n
                \en Set parameters of spline.\n \~
      \param[in] initDegree  - \ru Порядок сплайна.
                                \en A spline order. \~ 
      \param[in] initClosed  - \ru Признак замкнутости.
                                \en A closedness attribute. \~
      \param[in] initPoints  - \ru Набор контрольных точек.
                                \en Set of control points. \~ 
      \param[in] initWeights - \ru Набор весов для контрольных точек.
                                \en Set of weights for control points. \~
      \param[in] initKnots   - \ru Неубывающая последовательность весов.
                                \en Non-decreasing sequence of weights. \~
      \param[in] initForm    - \ru Тип построения.
                                \en Type of construction. \~
  */
  template <class PointsVector, class DoubleVector>
  bool         Init( size_t initDegree, bool initClosed, const PointsVector & initPoints, 
                     const DoubleVector & initWeights, const DoubleVector & initKnots, 
                     MbeNurbsCurveForm initForm = ncf_Unspecified )
  {
    if ( ::IsValidNurbsParamsExt( initDegree, initClosed, initPoints, &initWeights, &initKnots ) ) {
      Refresh(); // Must come first, since frees allocated memory

      degree = initDegree;
      closed = initClosed;
      pointList = initPoints;
      form = initForm;
      weights = initWeights;
      knots = initKnots;

      uppIndex = (ptrdiff_t)pointList.size() - 1;
      uppKnotsIndex = (ptrdiff_t)knots.size() - 1;

      SetClamped();
      return true;
    }
    return false;
  }

  /** \brief \ru Инициализация.
              \en Initialization. \~
      \details \ru Установить параметры сплайна.\n
                \en Set parameters of spline.\n \~
      \param[in] degree  - \ru Порядок сплайна.
                            \en A spline order. \~ 
      \param[in] cls     - \ru Признак замкнутости.
                            \en A closedness attribute. \~
      \param[in] points  - \ru Набор контрольных точек.
                            \en Set of control points. \~ 
      \param[in] knots   - \ru Неубывающая последовательность узлов.
                            \en Non-decreasing sequence of knots. \~
      \param[in] nPoints - \ru Количество контрольных точек.
                            \en Count of control points. \~
      \param[in] nKnots  - \ru Количество узлов.
                            \en Count of knots. \~
  */
  bool        Init( size_t degree, bool cls, const CcArray<double> & points,
                    const CcArray<double> & knots, ptrdiff_t nPoints, ptrdiff_t nKnots );

  /** \brief \ru Инициализация.
              \en Initialization. \~
      \details \ru Сплайн, проходящий через заданные точки при заданных параметрах.\n
        В случае замкнутости нужно передавать массив с совпадением первой и последней точек.\n
                \en Spline passing through given points at given parameters.\n
        In case of closedness it is necessary to pass the array with coincidence of the first and the last points.\n \~
      \param[in] degree  - \ru Порядок сплайна.
                            \en A spline order. \~
      \param[in] cls     - \ru Признак замкнутости.
                            \en A closedness attribute. \~
      \param[in] points  - \ru Набор точек, через которые проходит сплайн.
                            \en Set of points which the spline passes through. \~
      \param[in] params  - \ru Последовательность узловых параметров.
                            \en Sequence of knot parameters. \~
      \param[in] aKnots  - \ru Неубывающая последовательность узлов.
                            \en Nondecreasing sequence of knots. \~
  */
  bool        InitThrough(       size_t                degree, 
                                 bool                  cls, 
                           const SArray<MbCartPoint> & points,
                           const SArray<double> &      params, 
                                 SArray<double> *      aKnots = nullptr );

  /** \brief \ru Инициализация.
              \en Initialization. \~
      \details \ru Сплайн, проходящий через заданные точки при заданных параметрах.\n
        В случае замкнутости нужно передавать массив с совпадением первой и последней точек.\n
                \en Spline passing through given points at given parameters.\n
        In case of closedness it is necessary to pass the array with coincidence of the first and the last points.\n \~
      \param[in] degree  - \ru Порядок сплайна.
                            \en A spline order. \~
      \param[in] cls     - \ru Признак замкнутости.
                            \en A closedness attribute. \~
      \param[in] points  - \ru Набор точек, через которые проходит сплайн.
                            \en Set of points which the spline passes through. \~
      \param[in] params  - \ru Последовательность узловых параметров.
                            \en Sequence of knot parameters. \~
      \param[in] aKnots  - \ru Неубывающая последовательность узлов.
                            \en Nondecreasing sequence of knots. \~
  */
  bool        InitThrough(       size_t                   degree, 
                                 bool                     cls, 
                           const c3d::ParamPointsVector & points,
                           const c3d::DoubleVector &      params,
                                 c3d::DoubleVector *      aKnots = nullptr );
          
  /** \brief \ru Инициализация.
             \en Initialization. \~
      \details \ru Инициализировать прямолинейный сплайн.\n
               \en Initialize a straight spline. \n \~
      \param[in] t1  - \ru Начальный узел.
                       \en The initial knot. \~
      \param[in] p1  - \ru Начальная точка, через которую проходит сплайн.
                       \en The initial point the spline passes through. \~
      \param[in] t2  - \ru Конечный узел.
                       \en The final knot. \~
      \param[in] p2  - \ru Конечная точка, через которую проходит сплайн.
                       \en The final point the spline passes through. \~
  */
  bool        InitLine( double t1, const MbCartPoint & p1, double t2, const MbCartPoint & p2 );
  /** \brief \ru Инициализация.
             \en Initialization. \~
      \details \ru Инициализировать кубическую кривую как сплайн.\n
               \en Initialize a cubic curve as a spline. \n \~
      \param[in] p1  - \ru Начальная точка, через которую проходит сплайн.
                       \en The initial point the spline passes through. \~
      \param[in] v1  - \ru Касательный вектор к кривой в начальной точке.
                       \en A tangent vector to the curve at the start point. \~
      \param[in] p2  - \ru Конечная точка, через которую проходит сплайн.
                       \en The final point the spline passes through. \~
      \param[in] v2  - \ru Касательный вектор к кривой в конечной точке.
                       \en A tangent vector to the curve at the end point. \~
  */
  bool        InitCube( const MbCartPoint & p1, const MbVector & v1, const MbCartPoint & p2, const MbVector & v2 );

  // \ru Интерполяция. \en Interpolation. 

  /** \brief \ru Интерполяция.
             \en Interpolation. \~
      \details \ru Создать плоский сплайн второго порядка по точкам, параметрам и признаку замкнутости.
               \en Create a planar spline of second-order by points, parameters and attribute of closedness. \~
  */
  static  MbNurbs *   CreateNURBS2( const SArray<MbCartPoint> & points, const SArray<double> & params, bool cls );

  /// \ru Создать кубический NURBS по точкам, через которые он проходит, и параметрам сопряжения. \en Create cubic NURBS by parameters of conjugation and points which it passes through. 
  static  MbNurbs *   CreateNURBS4( const SArray<MbCartPoint> &, MbeSplineParamType spType,
                                    const c3d::PntMatingData2D & begData, 
                                    const c3d::PntMatingData2D & endData );
  /// \ru Создать кубический NURBS по интерполяционным точкам и данным сопряжения в каждой точке. \en Create cubic NURBS by interpolation points and data of conjugation at each point. 
  static  MbNurbs *   CreateNURBS4( const SArray<MbCartPoint> &, MbeSplineParamType spType,
                                    bool closed, RPArray<c3d::PntMatingData2D> & );
  /// \ru Создать кубический NURBS по интерполяционным точкам, их параметрам и данным сопряжения в каждой точке. \en Create cubic NURBS by interpolation points, parameters and data of conjugation at each point. 
  static  MbNurbs *   CreateNURBS4( const SArray<MbCartPoint> &, const SArray<double> &,
                                    bool closed, RPArray<c3d::PntMatingData2D> & );
  /** \brief \ru Интерполяция.
              \en Interpolation. \~
      \details \ru Создать плоский сплайн четвертого порядка по точкам, признаку замкнутости и типу параметризации.\n
                    Сплайн проходит через точки. Используется граничное условие отсутствия узла.
                \en Create a planar spline of fourth order by points, attribute of closedness and parametrization type.\n
                    NURBS passes through points. Used boundary condition of knot absence. \~
  */
  static  MbNurbs *   CreateNURBS4( const SArray<MbCartPoint> &, bool cls, MbeSplineParamType spType,
                                    MbeSplineCreateType useInitThrough = sct_Version2 );
  /** \brief \ru Интерполяция.
             \en Interpolation. \~
      \details \ru Создать плоский сплайн четвертого порядка по точкам, параметрам и признаку замкнутости.\n
                   Используется граничное условие отсутствия узла.
               \en Create a planar spline of fourth order by points, parameters and attribute of closedness.\n
                   Used boundary condition of knot absence. \~
  */
  static  MbNurbs *   CreateNURBS4( const SArray<MbCartPoint> & points, const SArray<double> & params, bool cls,
                                    MbeSplineCreateType useInitThrough = sct_Version2 );
  /** \brief \ru Интерполяция.
             \en Interpolation. \~
      \details \ru Создать плоский сплайн четвертого порядка по весам, точкам, параметрам и признаку замкнутости.\n
                   Используется граничное условие отсутствия узла.
               \en Create a planar spline of fourth order by weights, points, parameters and attribute of closedness.\n
                   Used boundary condition of knot absence. \~
  */
  static  MbNurbs *   CreateNURBS4( const SArray<double> & weights, const SArray<MbCartPoint> & points,
                                    SArray<double> & params, bool cls );

  /** \brief \ru Интерполяция.
             \en Interpolation. \~
      \details \ru Создать плоский сплайн четвертого порядка по точкам, параметрам и признаку замкнутости
        с граничными условиями - заданными векторами первых или вторых производных.\n
        Имеет 2 кратных внутренних узла, принадлежит классу дифференцируемых ( но не дважды дифференцируемых ) функций.
                \en Create a planar spline of fourth order by points, parameters and attribute of closedness
        with boundary conditions - given vectors of first or second derivatives.\n
        Has 2 multiple internal knots, belongs to class of differentiable (but not twice differentiable) functions. \~
        \param[in] bfstS  - \ru Если true, то начальное граничное условие - вектор первой производной, иначе - вектор второй производной.
                            \en If true, then start boundary condition is the vector of the first derivative, otherwise - the vector of the second derivative. \~
        \param[in] bfstN  - \ru Если true, то конечное граничное условие - вектор первой производной, иначе - вектор второй производной.
                            \en If true, then end boundary condition is the vector of first derivative, otherwise - vector of second derivative. \~
  */
  static  MbNurbs *   CreateNURBS4( const SArray<MbCartPoint> & points, const SArray<double> & params, 
                                    const MbVector &, const MbVector &, bool cls,
                                    bool bfstS = true, bool bfstN = true );

  /** \brief \ru Интерполяция.
             \en Interpolation. \~
      \details \ru Создать плоский сплайн четвертого порядка по точкам, производным, параметрам и признаку замкнутости.\n
                   Имеет 2 кратных внутренних узла, принадлежит классу дифференцируемых ( но не дважды дифференцируемых ) функций.
               \en Create a planar spline of fourth order by points, derivatives, parameters and attribute of closedness.\n
                   It has 2 multiple internal knots, belongs to the class of differentiable (but not twice differentiable) functions. \~
  */
  static  MbNurbs *    CreateNURBS4( const SArray<MbCartPoint> & points, const SArray<MbVector> & vectors,
                                     const SArray<double> & params, bool cls );

  /** \brief \ru Интерполяция.
             \en Interpolation. \~
      \details \ru Создать плоский сплайн четвертого порядка по составному сплайну Безье четвертого порядка.\n
        Внимание! Параметризация отлична от параметризации исходной кривой Безье.
               \en Create a planar spline of fourth order by composite Bezier spline of fourth order.\n
        If closedness is necessary - call UnClamped( bezier.IsClosed() ). \~
  */
  static  MbNurbs *   CreateNURBS4( const MbBezier & );

  /// \ru Установить сопряжение на конце. \en Set conjugation at the end. 
  bool        AttachG( c3d::PntMatingData2D & connectData, bool beg );

  /** \brief \ru Увеличить порядок кривой, не изменяя ее геометрическую форму и параметризацию.
             \en Increase order of curve without changing its geometric shape and parametrization. \~
      \details \ru Увеличить порядок кривой, не изменяя ее геометрическую форму и параметризацию. \n
               \en Increase order of curve without changing its geometric shape and parametrization. \n \~
      \param[in] newDegree - \ru Новый порядок сплайна.
                             \en New order of spline. \~
      \param[in] relEps - \ru Допустимая погрешность изменения формы.
                          \en Permissible shape error. \~
      \return \ru Возвращает true, если порядок сплайна был изменен.
              \en Returns true if the order of the spline was changed. \~
  */
  bool        RaiseDegree( size_t newDegree, double relEps = Math::paramEpsilon );

  /** \brief \ru Уменьшить порядок кривой на 1, не изменяя ее геометрическую форму и параметризацию.
             \en Decrease order of nurbs curve by 1 without changing its geometric shape and parametrization. \~
      \details \ru Уменьшить порядок кривой на 1, не изменяя ее геометрическую форму и параметризацию. \n
               \en Decrease order of nurbs curve by 1 without changing its geometric shape and parametrization. \n \~
      \param[in] relEps - \ru Допустимая погрешность изменения формы.
                          \en Permissible shape error. \~
      \return \ru Возвращает true, если порядок сплайна был изменен.
              \en Returns true if the order of the spline was changed. \~
  */
  bool        ReductionDegree( double relEps = Math::paramEpsilon );

  /** \brief \ru Задать порядок сплайна.
             \en Set the spline order. \~
      \details \ru Задать порядок сплайна. \n
                   При изменении порядка параметризация сплайна сбрасывается на равномерную, форма сплайна меняется. \n
               \en Set the spline order. \n
                   When you change the order, the parameterization of the spline is reset to uniform, the shape of the spline changes. \n \~
      \param[in] newDegree - \ru Новый порядок сплайна.
                             \en A new spline order. \~
      \return \ru Возвращает true, если порядок сплайна был изменен.
              \en Returns true if the order of the spline was changed. \~
  */
  bool        SetDegree( size_t newDegree );

  /// \ru Увеличить порядок  на 1. \en Increase the order by 1. 
  void        DegreeIncrease();
  /// \ru Установить тип формы. \en Set the type of shape. 
  void         SetFormType( MbeNurbsCurveForm f ) { form = f; }
  /// \ru Точка на кратном узле. \en The point on a multiple knot. 
  bool        PointOnMultipleKnot( const MbCartPoint & point ) const;

  /** \} */
  /** \ru \name Общие функции геометрического объекта.
      \en \name Common functions of geometric object.
      \{ */
  MbePlaneType  IsA() const override; // \ru Тип элемента. \en A type of element. 
  MbPlaneItem & Duplicate( MbRegDuplicate * = nullptr ) const override; // \ru Сделать копию элемента \en Create a copy of the element
  bool        IsSame( const MbPlaneItem & other, double accuracy = LENGTH_EPSILON ) const override; // \ru Является ли кривая other копией данной кривой? \en Whether the curve is duplicate of current curve. 
  bool        SetEqual( const MbPlaneItem & ) override;       // \ru Сделать элементы равными. \en Make elements equal.   
  void        Transform( const MbMatrix & matr, MbRegTransform * ireg = nullptr, const MbSurface * newSurface = nullptr ) override;  // \ru Преобразовать элемент согласно матрице. \en Transform element according to the matrix.
  void        Move( const MbVector & to, MbRegTransform * = nullptr, const MbSurface * newSurface = nullptr ) override; // \ru Сдвиг. \en Translation.
  void        Rotate( const MbCartPoint & pnt, const MbDirection & angle, MbRegTransform * = nullptr, const MbSurface * newSurface = nullptr ) override; // \ru Поворот. \en Rotation.

  void        GetProperties( MbProperties & ) override;  // \ru Выдать свойства объекта. \en Get properties of the object. 
  void        SetProperties( const MbProperties & ) override;  // \ru Записать свойства объекта. \en Set properties of the object. 
  void        GetBasisPoints( MbControlData & ) const override; // \ru Выдать контрольные точки объекта. \en Get control points of object.
  void        SetBasisPoints( const MbControlData & ) override; // \ru Изменить объект по контрольным точкам. \en Change the object by control points.

  /** \} */
  /** \ru \name Функции описания области определения кривой.
      \en \name Functions describing the domain of a curve.
      \{ */
  double      GetTMin()      const override; // \ru Вернуть минимальное значение параметра. \en Get the minimum value of parameter. 
  double      GetTMax()      const override; // \ru Вернуть максимальное значение параметра. \en Get the maximum value of parameter. 
  bool        IsClosed()     const override; // \ru Замкнутость кривой. \en A curve closedness. 
  bool        IsDegenerate( double eps = Math::LengthEps ) const override; // \ru Проверка вырожденности кривой. \en Check curve degeneracy. 
  bool        IsPeriodic()   const override; // \ru Периодичность замкнутой кривой. \en Periodicity of a closed curve.

  /** \} */
  /** \ru \name Функции для работы в области определения кривой. 
            Функции PointOn, FirstDer, SecondDer, ThirdDer,... корректируют параметр
            при выходе его за пределы области определения.
      \en \name Functions for working in the curve's domain. 
            Functions PointOn, FirstDer, SecondDer, ThirdDer,... correct parameter
            when it is out of domain bounds.
      \{ */
  void        PointOn  ( double & t, MbCartPoint & pnt ) const override; // \ru Точка на кривой. \en Point on the curve.
  void        FirstDer ( double & t, MbVector    & fd ) const override; // \ru Первая производная. \en First derivative. 
  void        SecondDer( double & t, MbVector    & sd ) const override; // \ru Вторая производная. \en Second derivative. 
  void        ThirdDer ( double & t, MbVector    & td ) const override; // \ru Третья производная. \en Third derivative. 
  /** \} */
  /** \ru \name Функции доступа к группе данных для работы внутри и вне области определения параметра кривой.
      \en \name Functions for get of the group of data inside and outside the curve's domain of parameter.
      \{ */
  // \ru Вычислить значения точки и производных для заданного параметра. \en Calculate point and derivatives of object for given parameter. \~
  void        Explore( double & t, bool ext,
                       MbCartPoint & pnt, MbVector & fir, MbVector * sec, MbVector * thir ) const override;
  // \ru Вычислить значения производных для заданного параметра. \en Calculate derivatives of object for given parameter. \~
  void        Derivatives( double & t, bool ext, MbVector & fir, MbVector * sec, MbVector * thi ) const;
          
  // \ru Функции, продолжающие кривую не по касательной как _PointOn() и др., а по кривой. \en Functions which do not continue curve along the tangent as _PointOn (), etc., and along a curve. 
  /// \ru Точка на продолжении кривой. \en Point on the curve extension. 
  void        ExtPointOn  ( double t, MbCartPoint & pnt ) const;
  /// \ru Первая производная на продолжении кривой. \en The first derivative on the curve extension. 
  void        ExtFirstDer ( double t, MbVector    & fd ) const;
  /// \ru Вторая производная на продолжении кривой. \en The second derivative on the curve extension. 
  void        ExtSecondDer( double t, MbVector    & sd ) const;
  /// \ru Третья производная на продолжении кривой. \en The third derivative on the curve extension. 
  void        ExtThirdDer ( double t, MbVector    & td ) const;

  /// \ru Продлить кривую. \en Extend the curve. \~
  MbResultType Extend( const MbCurveExtensionParameters & parameters, c3d::PlaneCurveSPtr & resCurve ) const override;

  /** \} */
  /** \ru \name Общие функции кривой
      \en \name Common functions of curve
      \{ */
  // \ru Добавить габарит в прямоугольник. \en Add a bounding box to rectangle.
  void        CalculateGabarit ( MbRect & ) const override;
  bool        IsStraight( bool ignoreParams = false ) const override; // \ru Прямолинейность кривой. \en Straightness of curve. 
  // \ru Посчитать метрическую длину \en Calculate the metric length 
  double      CalculateMetricLength() const override; 
  // \ru Сдвинуть параметр t на расстояние len. \en Move parameter t on the metric distance len. 
  bool        DistanceAlong( double & t, double len, int curveDir, double eps = Math::LengthEps,
                             VERSION version = Math::DefaultMathVersion() ) const override;
  // \ru Вычислить метрическую длину кривой.\en Calculate the metric length of a curve.
  double      CalculateLength( double t1, double t2 ) const override; 

  size_t      GetCount() const override;

  MbNurbs *   NurbsCurve( const MbCurveIntoNurbsInfo & ) const override;
  MbCurve *   NurbsCurve( const MbNurbsParameters & tParameters ) const override; // \ru Построить NURBS копию кривой. \en Construct a NURBS copy of a curve. 
  MbContour * NurbsContour() const override;

  // \ru Построить усеченную кривую c учетом заданной двумерной точности. \en Construct a trimmed curve with the given two-dimensional accuracy.
  MbCurve *   Trimmed( double t1, double t2, int sense, const MbDimAccuracy & xyEps, bool saveParamLenAndLaw ) const override;
  using MbCurve::Trimmed; // \ru Метод базового класса MbCurve без учета двумерной точности. Точность по умолчанию - MbDimAccuracy::twoDimAcc.  \en Method of MbCurve base class without two-dimensional accuracy. The default accuracy is MbDimAccuracy::twoDimAcc.

  void        Refresh() override; // \ru Сбросить все временные данные. \en Reset all temporary data. 
  void        Inverse( MbRegTransform * iReg = nullptr ) override; // \ru Изменение направления кривой на противоположное. \en Set the opposite direction of curve.

  // \ru Определить, является ли кривая репараметризованно такой же. \en Define whether a reparameterized curve is the same. 
  bool        IsReparamSame( const MbCurve & curve, double & factor ) const override;

  double      Step         ( double t, double sag ) const override; // \ru Вычисление шага аппроксимации с учетом радиуса кривизны. \en Calculation of the approximation step with consideration of the curvature radius. 
  double      DeviationStep( double t, double angle ) const override; // \ru Вычисление шага аппроксимации по угловой толерантности. \en Calculation of the approximation step by angular tolerance. 

  void        CalculatePolygon( double sag, MbPolygon & ) const override;

  MbeState    DeletePart( double t1, double t2, MbCurve *& part2 ) override; // \ru Удалить часть поликривой между параметрами t1 и t2. \en Remove a part of the polyline between t1 and t2 parameters. 
  MbeState    TrimmPart ( double t1, double t2, MbCurve *& part2 ) override; // \ru Оставить часть поликривой между параметрами t1 и t2. \en Save a part of the polyline between t1 and t2 parameters. 

  MbeState    Deformation( const MbRect &, const MbMatrix & ) override;  // \ru Деформация. \en Deformation. 
  bool        GoThroughPoint( MbCartPoint &p0 ) override; // \ru Прохождение сплайна через точку. \en Passing of spline through the point.
  void        TangentPoint( const MbCartPoint & pnt, SArray<double> & tFind ) const override; // \ru Вычисление всех касательных к кривой из данной точки. \en Calculation of all tangents to the curve from a given point. 
  void        OffsetCuspPoint( SArray<double> & tCusps, double dist ) const override; // \ru Определение особых точек офсетной кривой. \en Determination of singular points of the offset curve. 

  bool        GetCentre( MbCartPoint & ) const override; // \ru Выдать центр кривой. \en Give the curve center.
  double      GetRadius( double accuracy = PARAM_REGION ) const override; // \ru Дать физический радиус объекта или ноль, если это невозможно. \en Get the physical radius of the object or null if it impossible.
  bool        GetAxisPoint( MbCartPoint & p ) const override; // \ru Выдать центр оси кривой. \en Give the curve axis center. 
  bool        IsSimilarToCurve( const MbCurve & curve, double precision = PARAM_PRECISION ) const override; // \ru Подобные ли кривые для объединения (слива). \en Whether the curves to union (joining) are similar. 

  /// \ru Касание сплайна прямой. \en Touching the spline by line. 
  void        MakeTangentLine( MbLine * line );
  /// \ru Определить выпуклую оболочку сегмента кривой. \en Determine the convex hull of the curve segment. 
  void        ConvexHull( ptrdiff_t seg, MbCartPoint * p ) const; 
  /// \ru Получить границы участков кривой, которые описываются одной аналитической функцией. \en Get the boundaries of the curve sections that are described by one analytical function. \~
  void        GetAnalyticalFunctionsBounds( std::vector<double> & params ) const override;

  /** \} */
  /** \ru \name Общие функции полигональной кривой.
      \en \name Common functions of polygonal curve.
      \{ */

  void        GetRuleInterval( ptrdiff_t index, double & t1, double & t2 ) const override; // \ru Выдать интервал влияния точки B-сплайна. \en Get range of influence of B-spline point.

  void        Rebuild() override; // \ru Перестроить B-сплайн. \en Rebuild B-spline.
  void        SetClosed( bool cls ) override; // \ru Установить признак замкнутости. \en Set the closedness attribute.

  void        RemovePoint( ptrdiff_t index ) override; // \ru Удалить точку. \en Remove the point.
  void        RemovePoints() override; // \ru Удалить все точки. \en Delete all points. 

  void        AddPoint( const MbCartPoint & pnt ) override; // \ru Добавить точку в конец массива. \en Add point to the end of the array. 
  void        InsertPoint( ptrdiff_t index, const MbCartPoint & pnt ) override; // \ru Вставить точку по индексу. \en Insert a point by index. 
  void        InsertPoint( double t,  const MbCartPoint & pnt, double xEps, double yEps ) override; // \ru Вставить точку. \en Insert a point. 
  void        ChangePoint( ptrdiff_t index, const MbCartPoint & pnt ) override; // \ru Заменить вершину pointList[index] на pnt. \en Replace the vertex pointList[index] with pnt.
  bool        ChangePointsValue( const SArray<MbCartPoint> & pntList ) override; // \ru Заменить точки новыми при сохранении остальных параметров. \en Replace points with new one and save the other parameters.
  bool        CheckParam ( double & t, ptrdiff_t & i0, ptrdiff_t & i1, double & t0, double & t1 ) const override; // \ru Загнать параметр, получить локальный индексы и параметры. \en Move parameter, get local indices and parameters. 
  double      GetParam( ptrdiff_t i ) const override;
  size_t      GetParamsCount() const override; 

  void        ResetTCalc() const override; // \ru Сбросить текущее значение параметра \en Reset the current value of the parameter
  // \ru Расстояние до точки. \en The distance to a point. 
  double      DistanceToPoint( const MbCartPoint & to ) const override;
  // \ru Расстояние до точки, если оно меньше d. \en Distance to the point if it is less than d.
  bool        DistanceToPointIfLess( const MbCartPoint & to, double & d ) const override;
  // \ru Найти проекцию точки на кривую. \en Find the point projection to the curve. 
  double      PointProjection( const MbCartPoint & pnt ) const override;
  // \ru Найти проекцию точки на кривую. \en Find the point projection to the curve.
  bool        NearPointProjection( const MbCartPoint & pnt, double xEpsilon, double yEpsilon, 
                                   double & t, bool ext, MbRect1D * tRange = nullptr ) const override;

  bool        IsContinuousDerivative( bool & contLength, bool & contDirect, c3d::DoubleVector * params = nullptr, double epsilon = EPSILON ) const override; // \ru Непрерывна ли первая производная? \en Have the first derivative the continuous?
  // \ru Устранить разрывы первых производных по длине. \en Eliminate the discontinuities of the first derivative at length.
  bool        SetContinuousDerivativeLength( VERSION version, double epsilon = EPSILON ) override; 

  void        GetStartPoint( MbCartPoint & ) const override;
  void        GetEndPoint  ( MbCartPoint & ) const override;

  /** \} */
  /** \ru \name Функции B-сплайна.
      \en \name Functions of B-spline.
      \{ */

  /// \ru Добавить точку с весом. \en Add a point with weight. 
  void        AddPoint( ptrdiff_t index, const MbCartPoint & pnt, double weight );
  /// \ru Добавить точку в конец массива. \en Add point to the end of the array. 
  void        AddPoint( const MbCartPoint & pnt, double weight );
  /// \ru Сделать контур из NURBS-кривой. \en Create a contour from the NURBS curve. 
  MbContour * CreateContour() const;
  /// \ru Выделить часть. \en Break a part. 
  MbNurbs *   Break( double t1, double t2 ) const;

  /// \ru Получить форму В-сплайна. \en Get form of B-spline. 
  MbeNurbsCurveForm  GetFormType() const { return form; } 

  /// \ru Выдать порядок сплайна. \en Get the spline order.       
  size_t      GetDegree()  const { return degree; }
  /// \ru Вернуть признак рациональности, но не регулярности кривой. \en Get attribute of rationality, but no regularity of curve. 
  bool        IsRational() const;

  /// \ru Получить размер весового вектора. \en Get a size of weights vector. 
  size_t      GetWeightsCount()       const { return weights.size(); }
  /// \ru Получить весовой вектор. \en Get a weights vector. 
  template <class WeightsVector>
  void        GetWeights( WeightsVector & wts, bool justSet = true ) const { if ( justSet ) { wts.clear(); }; std::copy( weights.begin(), weights.end(), std::back_inserter( wts ) ); }
  /// \ru Получить значение элемента весового вектора по индексу. \en Get a weights vector element value by index. 
  double      GetWeight( size_t ind ) const { return weights[ind]; }
  /// \ru Получить значение элемента весового вектора по индексу. \en Get a weights vector element value by index. 
  double &    SetWeight( size_t ind )       { return weights[ind]; }

  /// \ru Получить размер узлового вектора. \en Get a size of knots vector. 
  size_t      GetKnotsCount()       const { return knots.size(); }
  /// \ru Получить узловой вектор. \en Get a knots vector. 
  template <class KnotsVector>
  void        GetKnots( KnotsVector & kts, bool justSet = true ) const { if ( justSet ) { kts.clear();  }; std::copy( knots.begin(), knots.end(), std::back_inserter( kts ) ); }
  /// \ru Получить значение элемента узлового вектора по индексу. \en Get a knots vector element value by index. 
  double      GetKnot( size_t ind ) const { return knots[ind]; }
  /// \ru Получить значение элемента узлового вектора по индексу. \en Get a knots vector element value by index. 
  double &    SetKnot( size_t ind )       { return knots[ind]; }
  /// \ru Вернуть максимальный индекс узлового вектора. \en Get the maximal index of knots vector.  
  ptrdiff_t   GetUppKnotsIndex()    const { return uppKnotsIndex; }

  // \ru BEG: для библиотеки (хорошо бы избавиться) \en BEG: for the library (it would be good to get rid of this) 
  /// \ru Добавить точку в конец массива. \en Add point to the end of the array. 
  void        LtAddPoint ( MbCartPoint & pnt, double weight ) { C3D_ASSERT_UNCONDITIONAL( false ); pointList.push_back( pnt ); weights.push_back( weight ); }
  /// \ru Добавить характерную точку в степенном представлении в конец массива. \en Add a control point with degree representation to the end of the array. 
  void        LtAddPowerPoint( MbCartPoint & pnt ) { C3D_ASSERT_UNCONDITIONAL( false ); pointList.push_back( pnt ); }
  /// \ru Добавить узел в конец узлового вектора. \en Add a knot to the end of knot vector. 
  void        LtAddKnot  ( double knot )           { C3D_ASSERT_UNCONDITIONAL( false ); knots.push_back( knot ); }
  /// \ru Задать порядок сплайна. \en Set the spline order. 
  void        LtSetDegree( size_t newDegree )   { C3D_ASSERT_UNCONDITIONAL( false ); if ( newDegree >= 2 && form == ncf_Unspecified ) { degree = newDegree; } }
  /// \ru Установить признак замкнутости. \en Set the closedness attribute. 
  void        LtSetClosed( bool cls )              { C3D_ASSERT_UNCONDITIONAL( false ); if ( form == ncf_Unspecified ) { closed = cls; } }
  /// \ru Изменить степень, замкнутость и тип формы. \en Change degree, closedness and type of shape. 
  void        LtSetData( size_t d, bool c, MbeNurbsCurveForm f );
  /// \ru Перестроить сплайн после накачки из библиотеки. \en Rebuild the spline. 
  bool        LtRebuild();
  /// \ru Инициализация. \en Initialization. 
  void        LtInit();
  // \ru Преобразование кусочно степенной формы в NURBS-кривую. \en Convert a piecewise exponential form to a NURBS-curve. 
  bool        LtInitPowerArc(); 
  bool        LtTrimmed( double t1, double t2, int sense = 1 );
  // \ru END: для библиотеки (хорошо бы избавиться) \en END: for the library (it would be good escape) 

  /// \ru Создать Bezier форму Nurbs. \en Create a Bezier shape of Nurbs. 
  void        Bezier( MbNurbs & bezierForm ) const;
  /// \ru Присоединить nurbs. \en Attach nurbs. 
  bool        Concatenate( MbNurbs & );

  /// \ru Задать вес для вершины. \en Set weight for control point. 
  void        SetWeight( ptrdiff_t pointNumber, double newWeight );

  /// \ru Получить кратность узла. \en Get the knot multiplicity. 
  size_t      KnotMultiplicity( ptrdiff_t knotIndex ) const;
  /// \ru Определение базисного узлового вектора. \en Determination of basis knot vector. 
  void        DefineKnotsVector();
  /// \ru Переопределение базисного узлового вектора из Close в Open. \en Redetermination of the basis knot vector from Close to Open. 
  bool        OpenKnotsVector();
  /// \ru Переопределение базисного узлового вектора из Open в Close. \en Redetermination of the basis knot vector from Open to Close. 
  bool        CloseKnotsVector();
  /// \ru Сдвинуть параметр замкнутого сплайна. \en Shift parameter of closed spline. 
  void        CyclicShift( ptrdiff_t interval );
  void        CyclicShift( double t );
  bool        BasicFunctions( double & t, ptrdiff_t k, CcArray<double> & values, ptrdiff_t & left, double & sum );                    

  void        CheckForm();
  /// \ru Преобразовать кривую в коническое сечение, если это возможно. \en Transform a curve into a conic section if it is possible. 
  MbCurve *   ConvertToConic();
  /// \ru Установить область изменения параметра. \en Set the range of parameter. 
  bool        SetLimitParam( double newTMin, double newTMax );

  // \ru Базовые операции над NURBS-кривой. \en Base operation with NURBS curve. 

  /// \ru Добавление нового узла; возвращает количество узлов, которые удалось вставить. \en Addition of a new knots; returns the number of knots which have been inserted. 
  size_t      InsertKnots( double & newKnot, size_t multiplicity, double relEps );
  /// \ru Удалить кратный внутренний узел id, num раз; вернуть количество удалений, которое удалось сделать. \en Remove multiple internal 'id' knot 'num' times, return count of removals was successfully made. 
  ptrdiff_t   RemoveKnot( ptrdiff_t id, ptrdiff_t num, double relEps = Math::paramEpsilon, double absEps = Math::lengthEpsilon );
  /// \ru Удалить все внутренние узлы, если это возможно. \en Remove all internal knots if it is possible. 
  void        RemoveAllKnots( double relEps = Math::paramEpsilon, double absEps = Math::lengthEpsilon );
  /// \ru Преобразовать данный nurbs в форму Безье, узловой вектор в зажатый. \en Convert this nurbs to Bezier form; knot vector to clamped. 
  bool        DecomposeCurve();

  /// \ru Преобразовать узловой вектор в зажатый (если кривая замкнута и clm = false) или разжатый (если кривая не замкнута и clm = true). \en Transform knot vector to clamped (if curve is closed and clm = false) or unclamped (if curve is open and clm = true). 
  bool        UnClamped( bool clm );
  /// \ru Добавить кривую в конец. \en Add curve to the end. 
  void        AddCurve ( MbNurbs &, bool bmerge = true );
  /// \ru Добавить кривые в конец. \en Add curves to the end. 
  template <class NurbsCurves>
  void        AddCurves( NurbsCurves & curves )
  {
    for ( size_t i = 0, icount = curves.size(); i < icount; ++i ) {
      if ( curves[i] != nullptr )
        AddCurve( *curves[i] );
    }
  }
  /** \brief \ru Разбить кривую.
              \en Split the curve. \~
      \details \ru Разбить недифференцируемую NURBS-кривую четвертой степени в трижды кратном внутреннем узле.\n
        Если внутренних трижды кратных узлов не существует, то в массив заносится копия кривой.\n
        Если bline = true, то проверить вырожденность в прямую, если прямая - преобразовать в прямую.
                \en Split the non-differentiable NURBS-curve of fourth degree at internal knot with multiplicity of three.\n
        If there are no internal knots with multiplicity of three, then the array is filled with copy of curve.\n
        If bline = true, then check the curve for degeneration into a line, if it is a line then transform to a line. \~
  */
  bool        BreakC0NURBS4( RPArray<MbCurve> &, bool bline = true );
  /// \ru Разбить NURBS-кривую в местах, где кривая не дифференцируема. Параметризация не сохраняется. \en Split NURBS-curve at places where the curve is non-differentiable. Parametrization does not remain. 
  bool        BreakC0( c3d::PlaneCurvesSPtrVector &, double metricAcc = METRIC_EPSILON, VERSION version = Math::DefaultMathVersion() );
  /// \ru Расширить незамкнутую NURBS-кривую по касательным. \en Extend open NURBS-curve by tangents. 
  bool        ExtendNurbs( double, double, bool bmerge = false );

  /** \brief \ru Замкнуть кривую.
              \en Make curve closed. \~
      \details \ru Замкнуть фактически замкнутую кривую.\n
        То есть если первая и последняя точки кривой совпадают, но она реализована как незамкнутая,
        то одна из совпадающих точек убирается и кривая делается замкнутой.
                \en Make actually closed curve closed.\n
        That is, if the first and the last points of curve are coincident, but curve implemented as open,
        then one of coincident points is taken away and curve becomes closed. \~
  */
  void        FixClosedNurbs();

  /** \} */

protected:
  bool        CanChangeClosed() const override; // \ru Можно ли поменять признак замкнутости. // ЯТ К6 \en Whether it is possible to change the attribute of closedness. // ЯТ К6

private: // \ru Системные методы. \en System methods. 
  bool        CatchMemory( MbNurbsAuxiliaryData * cache  ) const; // \ru Выделить память. \en Allocate memory. 
  void        FreeMemory( MbNurbsAuxiliaryData * cache  )  const; // \ru Освободить память. \en Free memory. 
  void        VerifyParam( double & t ) const; // \ru Загнать параметр t в параметрическую область кривой. \en Parameter set in the curve region. 
  void        CalculateSegment( double & t, MbNurbsAuxiliaryData * cache  ) const; // \ru Рассчитать базисные функции и разностные формы на участке. \en Calculate the basis functions and differential forms on the region. 
  void        CalculateSpline( ptrdiff_t n, MbNurbsAuxiliaryData * cache  ) const;   // \ru Рассчитать точку NURBS-кривой или производную n-го порядка. \en Calculate point of NURBS-curve or n-th order derivative. 
  void        CalculateSplineWeight( double & t, ptrdiff_t n, MbNurbsAuxiliaryData * cache  ) const;
  bool        InitSegments( MbNurbsAuxiliaryData * cache  ) const;

          /** \brief \ru Инициализация.
                     \en Initialization. \~
              \details \ru Сплайн, проходящий через заданные точки при заданных параметрах.\n
                В случае замкнутости нужно передавать массив с совпадением первой и последней точек.\n
                       \en Spline passing through given points at given parameters.\n
                In case of closedness it is necessary to pass the array with coincidence of the first and the last points.\n \~
              \param[in] degree  - \ru Порядок сплайна.
                                   \en A spline order. \~
              \param[in] cls     - \ru Признак замкнутости.
                                   \en A closedness attribute. \~
              \param[in] points  - \ru Набор точек, через которые проходит сплайн.
                                   \en Set of points which the spline passes through. \~
              \param[in] params  - \ru Последовательность узловых параметров.
                                   \en Sequence of knot parameters. \~
          */
          template <class PointsVector, class ParamsVector>
  bool        InitThroughTempl(       size_t         degree, 
                                      bool           cls, 
                                        const PointsVector & points,
                                        const ParamsVector & params,
                                              ParamsVector * aKnots = nullptr );

          // \ru Служебные аналоги публичных функций, которые используют заданный кэш. \en Service analogs of public functions that use a given cache.
  void        PointOn( double & t, MbCartPoint & pnt, MbNurbsAuxiliaryData * ucache ) const; // \ru Точка на кривой. \en Point on the curve. 
  void        FirstDer( double & t, MbVector    & fd, MbNurbsAuxiliaryData * ucache ) const; // \ru Первая производная. \en First derivative. 
  void        SecondDer( double & t, MbVector    & sd, MbNurbsAuxiliaryData * ucache ) const; // \ru Вторая производная. \en Second derivative. 
  void        Derivatives( double & t, bool ext, MbVector & fir, MbVector * sec, MbVector * thi, MbNurbsAuxiliaryData * ucache ) const;

  void        SetClamped(); // \ru Делаем зажатый узловой вектор. \en Set clamped knots vector. 

  void        ResetCache(); // \ru Очистить кэш главного потока, сбросить остальные кэши. \en Clear main thread cache, reset other caches. 
  bool        NurbsPlus( MbNurbs & nurbs, double tin, double tax, double tRegion ) const;

          // \ru Расчет весовых функций и их первых производных. \en Calculation of weight functions and its first derivatives. 
  ptrdiff_t   WeightFunctions( double & x, CcArray<double> & ) const;
          // \ru Вычисление шага аппроксимации в обе стороны. \en Calculation of approximation step in both directions. 
  double      StepD( double & t, double sag, bool checkAngle = false, double angle = 0.0, MbNurbsAuxiliaryData * cache = nullptr ) const;
          // \ru Вычисление шага аппроксимации сплайна второго порядка. \en Calculation of approximation step of second order spline. 
  double      PolylineStep( double t, bool half, MbNurbsAuxiliaryData * cache ) const;
          // \ru Уточнить проекцию \en Specify projection.
  double      SpecifyProjection( const MbCartPoint & pnt, double xEpsilon, double yEpsilon, double t, bool ext ) const;

  void        operator = ( const MbNurbs & ); // \ru Не реализовано. \en Not implemented. 

  DECLARE_PERSISTENT_CLASS_NEW_DEL( MbNurbs )
};

IMPL_PERSISTENT_OPS( MbNurbs )


//------------------------------------------------------------------------------
// Конструктор математического В-сплайна.
// ---
template <class PointsVector, class DoubleVector>
MbNurbs::MbNurbs( size_t initDegree, bool initClosed, const PointsVector & initPoints, 
                  const DoubleVector * initWeights, const DoubleVector * initKnots )
  : MbPolyCurve  (           )
  , degree       ( 0         )
  , uppKnotsIndex( SYS_MAX_T ) // Максимальный индекс узлового вектора.
  , knots        (           )
  , weights      (           )
  , form         ( ncf_Unspecified ) // Форма В-сплайна.
  , cache        (           )
{
  if ( ::IsValidNurbsParamsExt( initDegree, initClosed, initPoints, initWeights, initKnots ) ) {
    pointList.assign( initPoints.begin(), initPoints.end() ); 
    uppIndex  = (ptrdiff_t)pointList.size() - 1; // Количество точек.

    closed    = initClosed;
    degree    = initDegree; // Степень В-сплайна.

    if ( initWeights != nullptr )
      weights.assign( initWeights->begin(), initWeights->end() );
    else
      weights.assign( initPoints.size(), 1.0 );

    if ( initKnots != nullptr ) {
      knots.assign( initKnots->begin(), initKnots->end() );
      uppKnotsIndex = (ptrdiff_t)knots.size() - 1;
    }
    else {
      DefineKnotsVector();
    }

    Refresh();
  }
}


//------------------------------------------------------------------------------
// \ru Добавить точку в конец массива. \en Add point to the end of the array. 
// ---
inline
void MbNurbs::AddPoint( const MbCartPoint & pnt, double weight )
{
  pointList.push_back( pnt );
  weights.push_back( weight );
  form = ncf_Unspecified;
  Rebuild();
}


//------------------------------------------------------------------------------
// \ru Является ли сплайн прямолинейным \en Whether the spline is straight 
// ---
template <class Nurbs, class Point, class Vector>
bool IsStraightNurbs( const Nurbs & nurbs, double mEps = METRIC_EPSILON )
{
  bool isStraight = false;

  if ( !nurbs.IsClosed() ) {
    size_t wtsCnt = nurbs.GetWeightsCount();

    isStraight = true;
    if ( wtsCnt > 1 ) {
      double wt0 = nurbs.GetWeight(0);
      for ( size_t k = 1; k < wtsCnt; k++ ) {
        double wt = nurbs.GetWeight(k);
        if ( ::fabs(wt0 - wt) > EXTENT_EQUAL ) {
          isStraight = false;
          break;
        }
      }
    }
    if ( isStraight ) {
      isStraight = false;
      std::vector<Point> pnts;
      pnts.reserve( nurbs.GetPointsCount() );
      nurbs.GetPointList( pnts );
      if ( c3d::ArePointsOnLine<Point, Vector>( pnts, mEps ) )
        isStraight = true;
    }
  }

  return isStraight;
}


#endif // __CUR_NURBS_H
