Skip to content

Latest commit

 

History

History
1117 lines (922 loc) · 38.1 KB

curve.rst

File metadata and controls

1117 lines (922 loc) · 38.1 KB

curve module

This is a collection of procedures for performing computations on a Bézier curve.

Procedures

.. c:function:: void BEZ_compute_length(const int *num_nodes, \
                                        const int *dimension, \
                                        const double *nodes, \
                                        double *length, \
                                        int *error_val)

   Computes the length of a B |eacute| zier curve via

   .. math::

      \ell = \int_0^1 \left\lVert B'(s) \right\rVert \, ds.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param length:
      **[Output]** The computed length :math:`\ell`.
   :type length: :c:expr:`double*`
   :param error_val:
      **[Output]** An error status passed along from ``dqagse`` (a QUADPACK
      procedure).
   :type error_val: :c:expr:`int*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_compute_length(const int *num_nodes,
                         const int *dimension,
                         const double *nodes,
                         double *length,
                         int *error_val);

   **Example:**

   .. literalinclude:: example_compute_length.c
      :language: c
      :dedent: 4
      :lines: 18-29

   Consider the line segment :math:`B(s) = \left[\begin{array}{c} 3s \\ 4s
   \end{array}\right]`, we can verify the length:

   .. testsetup:: example-compute-length, example-elevate-nodes-curve,
                  example-evaluate-curve-barycentric,
                  example-evaluate-hodograph, example-evaluate-multi,
                  example-full-reduce, example-get-curvature,
                  example-locate-point-curve, example-newton-refine-curve,
                  example-reduce-pseudo-inverse, example-specialize-curve,
                  example-subdivide-nodes-curve

      import tests.utils


      build_and_run_c = tests.utils.build_and_run_c

   .. testcode:: example-compute-length
      :hide:

      build_and_run_c("example_compute_length.c")

   .. testoutput:: example-compute-length
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_compute_length.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      Length: 5.000000
      Error value: 0

.. c:function:: void BEZ_elevate_nodes_curve(const int *num_nodes, \
                                             const int *dimension, \
                                             const double *nodes, \
                                             double *elevated)

   Degree-elevate a B |eacute| zier curve. Does so by producing
   control points of a higher degree that define the exact same curve.

   See :meth:`.Curve.elevate` for more details.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param elevated:
      **[Output]** The control points of the degree-elevated curve as a
      :math:`D \times (N + 1)` array, laid out in Fortran order.
   :type elevated: :c:expr:`double*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_elevate_nodes_curve(const int *num_nodes,
                              const int *dimension,
                              const double *nodes,
                              double *elevated);

   **Example:**

   After elevating :math:`B(s) = \left[\begin{array}{c} 0 \\ 0
   \end{array}\right] (1 - s)^2 + \frac{1}{2} \left[\begin{array}{c} 3 \\ 3
   \end{array}\right] 2 (1 - s) s + \left[\begin{array}{c} 3 \\ 0
   \end{array}\right] s^2`:

   .. literalinclude:: example_elevate_nodes_curve.c
      :language: c
      :dedent: 4
      :lines: 18-31

   we have :math:`B(s) = \left[\begin{array}{c} 0 \\ 0
   \end{array}\right] (1 - s)^3 + \left[\begin{array}{c} 1 \\ 1
   \end{array}\right] 3 (1 - s)^2 s + \left[\begin{array}{c} 2 \\ 1
   \end{array}\right] 3 (1 - s) s^2 + \left[\begin{array}{c} 3 \\ 0
   \end{array}\right] s^3`:

   .. testcode:: example-elevate-nodes-curve
      :hide:

      build_and_run_c("example_elevate_nodes_curve.c")

   .. testoutput:: example-elevate-nodes-curve
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_elevate_nodes_curve.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      Elevated:
      0.000000, 1.000000, 2.000000, 3.000000
      0.000000, 1.000000, 1.000000, 0.000000

   .. image:: ../images/curve_elevate.png
      :align: center

.. c:function:: void BEZ_evaluate_curve_barycentric(const int *num_nodes, \
                                                    const int *dimension, \
                                                    const double *nodes, \
                                                    const int *num_vals, \
                                                    const double *lambda1, \
                                                    const double *lambda2, \
                                                    double *evaluated)

   For a B |eacute| zier curve with control points :math:`p_0, \ldots, p_d`,
   this evaluates the quantity

   .. math::

      Q(\lambda_1, \lambda_2) =
          \sum_{j = 0}^d \binom{d}{j} \lambda_1^{d - j} \lambda_2^j p_j.

   The typical case is barycentric, i.e. :math:`\lambda_1 + \lambda_2 = 1`, but
   this is not required.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param num_vals:
      **[Input]** The number of values :math:`k` where the quantity will be
      evaluated.
   :type num_vals: :c:expr:`const int*`
   :param lambda1:
      **[Input]** An array of :math:`k` values used for the first parameter
      :math:`\lambda_1`.
   :type lambda1: :c:expr:`const double*`
   :param lambda2:
      **[Input]** An array of :math:`k` values used for the second parameter
      :math:`\lambda_2`.
   :type lambda2: :c:expr:`const double*`
   :param evaluated:
      **[Output]** The evaluated quantites as a :math:`D \times k` array, laid
      out in Fortran order. Column :math:`j` of ``evaluated`` will contain
      :math:`Q\left(\lambda_1\left[j\right], \lambda_2\left[j\right]\right)`.
   :type evaluated: :c:expr:`double*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_evaluate_curve_barycentric(const int *num_nodes,
                                     const int *dimension,
                                     const double *nodes,
                                     const int *num_vals,
                                     const double *lambda1,
                                     const double *lambda2,
                                     double *evaluated);

   **Example:**

   For the curve :math:`B(s) = \left[\begin{array}{c} 0 \\ 1
   \end{array}\right] (1 - s)^2 + \left[\begin{array}{c} 2 \\ 1
   \end{array}\right] 2 (1 - s) s + \left[\begin{array}{c} 3 \\ 3
   \end{array}\right] s^2 = \left[\begin{array}{c} s(4 - s) \\ 2s^2 + 1
   \end{array}\right]`:

   .. literalinclude:: example_evaluate_curve_barycentric.c
      :language: c
      :dedent: 4
      :lines: 18-35

   we have

   .. math::

      \begin{align*}
        Q\left(\frac{1}{4}, \frac{3}{4}\right) &= \frac{1}{16} \left[
          \begin{array}{c} 39 \\ 34 \end{array}\right] \\
        Q\left(\frac{1}{2}, \frac{1}{4}\right) &= \frac{1}{16} \left[
          \begin{array}{c} 11 \\ 11 \end{array}\right] \\
        Q\left(0, \frac{1}{2}\right) &= \frac{1}{4} \left[
          \begin{array}{c} 3 \\ 3 \end{array}\right] \\
        Q\left(1, \frac{1}{4}\right) &= \frac{1}{16} \left[
          \begin{array}{c} 19 \\ 27 \end{array}\right]
      \end{align*}

   .. testcode:: example-evaluate-curve-barycentric
      :hide:

      build_and_run_c("example_evaluate_curve_barycentric.c")

   .. testoutput:: example-evaluate-curve-barycentric
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_evaluate_curve_barycentric.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      Evaluated:
      2.437500, 0.687500, 0.750000, 1.187500
      2.125000, 0.687500, 0.750000, 1.687500

.. c:function:: void BEZ_evaluate_hodograph(const double *s, \
                                            const int *num_nodes, \
                                            const int *dimension, \
                                            const double *nodes, \
                                            double *hodograph)

   Evaluates the hodograph (or derivative) of a B |eacute| zier curve
   function :math:`B'(s)`.

   :param s:
      **[Input]** The parameter :math:`s` where the hodograph is being
      computed.
   :type s: :c:expr:`const double*`
   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param hodograph:
      **[Output]** The hodograph :math:`B'(s)` as a :math:`D \times 1` array.
   :type hodograph: :c:expr:`double*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_evaluate_hodograph(const double *s,
                             const int *num_nodes,
                             const int *dimension,
                             const double *nodes,
                             double *hodograph);

   **Example:**

   For the curve :math:`B(s) = \left[\begin{array}{c} 1 \\ 0
   \end{array}\right] (1 - s)^3 + \left[\begin{array}{c} 1 \\ 1
   \end{array}\right] 3 (1 - s)^2 s + \left[\begin{array}{c} 2 \\ 0
   \end{array}\right] 3 (1 - s) s^2 + \left[\begin{array}{c} 2 \\ 1
   \end{array}\right] s^3`:

   .. literalinclude:: example_evaluate_hodograph.c
      :language: c
      :dedent: 4
      :lines: 18-28

   we have :math:`B'\left(\frac{1}{8}\right) = \frac{1}{32} \left[
   \begin{array}{c} 21 \\ 54 \end{array}\right]`:

   .. testcode:: example-evaluate-hodograph
      :hide:

      build_and_run_c("example_evaluate_hodograph.c")

   .. testoutput:: example-evaluate-hodograph
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_evaluate_hodograph.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      Hodograph:
      0.656250
      1.687500

.. c:function:: void BEZ_evaluate_multi(const int *num_nodes, \
                                        const int *dimension, \
                                        const double *nodes, \
                                        const int *num_vals, \
                                        const double *s_vals, \
                                        double *evaluated)

   Evaluate a B |eacute| zier curve function :math:`B(s_j)` at
   multiple values :math:`\left\{s_j\right\}_j`.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param num_vals:
      **[Input]** The number of values :math:`k` where the :math:`B(s)` will be
      evaluated.
   :type num_vals: :c:expr:`const int*`
   :param s_vals:
      **[Input]** An array of :math:`k` values :math:`s_j`.
   :type s_vals: :c:expr:`const double*`
   :param evaluated:
      **[Output]** The evaluated points as a :math:`D \times k` array, laid
      out in Fortran order. Column :math:`j` of ``evaluated`` will contain
      :math:`B\left(s_j\right)`.
   :type evaluated: :c:expr:`double*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_evaluate_multi(const int *num_nodes,
                         const int *dimension,
                         const double *nodes,
                         const int *num_vals,
                         const double *s_vals,
                         double *evaluated);

   **Example:**

   For the curve :math:`B(s) = \left[\begin{array}{c} 1 \\ 0
   \end{array}\right] (1 - s)^3 + \left[\begin{array}{c} 1 \\ 1
   \end{array}\right] 3 (1 - s)^2 s + \left[\begin{array}{c} 2 \\ 0
   \end{array}\right] 3 (1 - s) s^2 + \left[\begin{array}{c} 2 \\ 1
   \end{array}\right] s^3`:

   .. literalinclude:: example_evaluate_multi.c
      :language: c
      :dedent: 4
      :lines: 18-32

   we have :math:`B\left(0\right) = \left[\begin{array}{c}
   1 \\ 0 \end{array}\right], B\left(\frac{1}{2}\right) =
   \frac{1}{2} \left[\begin{array}{c} 3 \\ 1 \end{array}\right]` and
   :math:`B\left(1\right) = \left[\begin{array}{c} 2 \\ 1 \end{array}\right]`:

   .. testcode:: example-evaluate-multi
      :hide:

      build_and_run_c("example_evaluate_multi.c")

   .. testoutput:: example-evaluate-multi
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_evaluate_multi.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      Evaluated:
      1.000000, 1.500000, 2.000000
      0.000000, 0.500000, 1.000000

.. c:function:: void BEZ_full_reduce(const int *num_nodes, \
                                     const int *dimension, \
                                     const double *nodes, \
                                     const int *num_reduced_nodes, \
                                     double *reduced, \
                                     bool *not_implemented)

   Perform a "full" degree reduction. Does so by using
   :c:func:`BEZ_reduce_pseudo_inverse` continually until the degree of
   the curve can no longer be reduced.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param num_reduced_nodes:
      **[Output]** The number of control points :math:`M` of the fully reduced
      curve.
   :type num_reduced_nodes: :c:expr:`const int*`
   :param reduced:
      **[Output]** The control points of the fully reduced curve as a
      :math:`D \times N` array. The first :math:`M` columns will contain the
      reduced nodes. ``reduced`` must be allocated by the caller and since
      :math:`M = N` occurs when no reduction is possible, this array must be
      :math:`D \times N`.
   :type reduced: :c:expr:`double*`
   :param not_implemented:
      **[Output]** Indicates if degree-reduction has been implemented for the
      current curve's degree. (Currently, the only degrees supported are 1,
      2, 3 and  4.)
   :type not_implemented: :c:expr:`bool*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_full_reduce(const int *num_nodes,
                      const int *dimension,
                      const double *nodes,
                      const int *num_reduced_nodes,
                      double *reduced,
                      bool *not_implemented);

   **Example:**

   When taking a curve that is degree-elevated from linear to quartic:

   .. literalinclude:: example_full_reduce.c
      :language: c
      :dedent: 4
      :lines: 18-34

   this procedure reduces it to the line
   :math:`B(s) = \left[\begin{array}{c} 1 \\ 3
   \end{array}\right] (1 - s) + \left[\begin{array}{c} 2 \\ 5
   \end{array}\right] s = \left[\begin{array}{c} 1 + s \\ 3 + 2s
   \end{array}\right]`:

   .. testcode:: example-full-reduce
      :hide:

      build_and_run_c("example_full_reduce.c")

   .. testoutput:: example-full-reduce
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_full_reduce.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      Number of reduced nodes: 2
      Reduced:
      1.000000, 2.000000
      3.000000, 5.000000
      Not implemented: FALSE

.. c:function:: void BEZ_get_curvature(const int *num_nodes, \
                                       const double *nodes, \
                                       const double *tangent_vec, \
                                       const double *s, \
                                       double *curvature)

   Get the signed curvature of a B |eacute| zier curve at a point. See
   :func:`.hazmat.curve_helpers.get_curvature` for more details.

   .. note::

      This **only** computes curvature for plane curves (i.e. curves
      in :math:`\mathbf{R}^2`). An equivalent notion of curvature exists for
      space curves, but support for that is not implemented here.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`2 \times N` array. This should be laid out in Fortran order,
      with :math:`2 N` total values.
   :type nodes: :c:expr:`const double*`
   :param tangent_vec:
      **[Input]** The hodograph :math:`B'(s)` as a :math:`2 \times 1` array.
      Note that this could be computed once :math:`s` and :math:`B` are known,
      but this allows the caller to re-use an already computed tangent vector.
   :type tangent_vec: :c:expr:`const double*`
   :param s:
      **[Input]** The parameter :math:`s` where the curvature is being
      computed.
   :type s: :c:expr:`const double*`
   :param curvature:
      **[Output]** The signed curvature :math:`\kappa`.
   :type curvature: :c:expr:`double*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_get_curvature(const int *num_nodes,
                        const double *nodes,
                        const double *tangent_vec,
                        const double *s,
                        double *curvature);

   **Example:**

   .. literalinclude:: example_get_curvature.c
      :language: c
      :dedent: 4
      :lines: 18-28

   .. image:: ../images/get_curvature.png
      :align: center

   .. testcode:: example-get-curvature
      :hide:

      build_and_run_c("example_get_curvature.c")

   .. testoutput:: example-get-curvature
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_get_curvature.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      Curvature: -12.000000

.. c:function:: void BEZ_locate_point_curve(const int *num_nodes, \
                                            const int *dimension, \
                                            const double *nodes, \
                                            const double *point, \
                                            double *s_approx)

   This solves the inverse problem :math:`B(s) = p` (if it can be
   solved). Does so by subdividing the curve until the segments are
   sufficiently small, then using Newton's method to narrow in on the
   pre-image of the point.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param point:
      **[Input]** The point :math:`p` as a :math:`D \times 1` array.
   :type point: :c:expr:`const double*`
   :param s_approx:
      **[Output]** The parameter :math:`s` of the solution. If
      :math:`p` can't be located on the curve, then ``s_approx = -1.0``.
      If there are **multiple** parameters that satisfy :math:`B(s) = p`
      (indicating that :math:`B(s)` has a self-crossing) then
      ``s_approx = -2.0``.
   :type s_approx: :c:expr:`double*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_locate_point_curve(const int *num_nodes,
                             const int *dimension,
                             const double *nodes,
                             const double *point,
                             double *s_approx);

   **Example:**

   For :math:`B(s) = \left[\begin{array}{c} 0 \\ 2
   \end{array}\right] (1 - s)^3 + \left[\begin{array}{c} -1 \\ 0
   \end{array}\right] 3 (1 - s)^2 s + \left[\begin{array}{c} 1 \\ 1
   \end{array}\right] 3 (1 - s) s^2 + \frac{1}{8} \left[\begin{array}{c}
   -6 \\ 13 \end{array}\right] s^3`:

   .. literalinclude:: example_locate_point_curve.c
      :language: c
      :dedent: 4
      :lines: 18-34

   We can locate the point :math:`B\left(\frac{1}{2}\right) = \frac{1}{64}
   \left[\begin{array}{c} -6 \\ 53 \end{array}\right]` but find that
   :math:`\frac{1}{2} \left[\begin{array}{c} 0 \\ 3 \end{array}\right]` is
   not on the curve and that

   .. math::

      B\left(\frac{3 - \sqrt{5}}{6}\right) =
          B\left(\frac{3 + \sqrt{5}}{6}\right) = \frac{1}{8} \left[
          \begin{array}{c} -2 \\ 11 \end{array}\right]

   is a self-crossing:

   .. testcode:: example-locate-point-curve
      :hide:

      build_and_run_c("example_locate_point_curve.c")

   .. testoutput:: example-locate-point-curve
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_locate_point_curve.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      When B(s) = [-0.093750, 0.828125]; s =  0.500000
      When B(s) = [ 0.000000, 1.500000]; s = -1.000000
      When B(s) = [-0.250000, 1.375000]; s = -2.000000

   .. image:: ../images/curve_locate.png
      :align: center

.. c:function:: void BEZ_newton_refine_curve(const int *num_nodes, \
                                             const int *dimension, \
                                             const double *nodes, \
                                             const double *point, \
                                             const double *s, \
                                             double *updated_s)

   This refines a solution to :math:`B(s) = p` using Newton's
   method. Given a current approximation :math:`s_n` for a solution,
   this produces the updated approximation via

   .. math::

      s_{n + 1} = s_n - \frac{B'(s_n)^T \left[B(s_n) - p\right]}{
          B'(s_n)^T B'(s_n)}.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param point:
      **[Input]** The point :math:`p` as a :math:`D \times 1` array.
   :type point: :c:expr:`const double*`
   :param s:
      **[Input]** The parameter :math:`s_n` of the current approximation
      of a solution.
   :type s: :c:expr:`const double*`
   :param updated_s:
      **[Output]** The parameter :math:`s_{n + 1}` of the updated
      approximation.
   :type updated_s: :c:expr:`double*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_newton_refine_curve(const int *num_nodes,
                              const int *dimension,
                              const double *nodes,
                              const double *point,
                              const double *s,
                              double *updated_s);

   **Example:**

   When trying to locate :math:`B\left(\frac{1}{4}\right) = \frac{1}{16}
   \left[\begin{array}{c} 9 \\ 13 \end{array}\right]` on the curve
   :math:`B(s) = \left[\begin{array}{c} 0 \\ 0
   \end{array}\right] (1 - s)^2 + \left[\begin{array}{c} 1 \\ 2
   \end{array}\right] 2 (1 - s) s + \left[\begin{array}{c} 3 \\ 1
   \end{array}\right] s^2`, starting at :math:`s = \frac{3}{4}`:

   .. literalinclude:: example_newton_refine_curve.c
      :language: c
      :dedent: 4
      :lines: 18-29

   we expect a Newton update :math:`\Delta s = -\frac{2}{5}`, which produces
   a new parameter value :math:`s = \frac{7}{20}`:

   .. testcode:: example-newton-refine-curve
      :hide:

      build_and_run_c("example_newton_refine_curve.c")

   .. testoutput:: example-newton-refine-curve
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_newton_refine_curve.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      Updated s: 0.350000

   .. image:: ../images/newton_refine_curve.png
      :align: center

.. c:function:: void BEZ_reduce_pseudo_inverse(const int *num_nodes, \
                                               const int *dimension, \
                                               const double *nodes, \
                                               double *reduced, \
                                               bool *not_implemented)

   Perform a pseudo inverse to :c:func:`BEZ_elevate_nodes_curve`. If an
   inverse can be found, i.e. if a curve can be degree-reduced, then
   this will produce the equivalent curve of lower degree. If no
   inverse can be found, then this will produce the "best" answer in
   the least squares sense.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param reduced:
      **[Output]** The control points of the degree-(pseudo)reduced curve
      :math:`D \times (N - 1)` array, laid out in Fortran order.
   :type reduced: :c:expr:`double*`
   :param not_implemented:
      **[Output]** Indicates if degree-reduction has been implemented for the
      current curve's degree. (Currently, the only degrees supported are 1,
      2, 3 and  4.)
   :type not_implemented: :c:expr:`bool*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_reduce_pseudo_inverse(const int *num_nodes,
                                const int *dimension,
                                const double *nodes,
                                double *reduced,
                                bool *not_implemented);

   **Example:**

   After reducing :math:`B(s) = \left[\begin{array}{c} -3 \\ 3
   \end{array}\right] (1 - s)^3 + \left[\begin{array}{c} 0 \\ 2
   \end{array}\right] 3 (1 - s)^2 s + \left[\begin{array}{c} 1 \\ 3
   \end{array}\right] 3 (1 - s) s^2 + \left[\begin{array}{c} 0 \\ 6
   \end{array}\right] s^3`:

   .. literalinclude:: example_reduce_pseudo_inverse.c
      :language: c
      :dedent: 4
      :lines: 18-32

   we get the valid quadratic representation of :math:`B(s) =
   \left[\begin{array}{c} 3(1 - s)(2s - 1) \\ 3(2s^2 - s + 1)
   \end{array}\right]`:

   .. testcode:: example-reduce-pseudo-inverse
      :hide:

      build_and_run_c("example_reduce_pseudo_inverse.c")

   .. testoutput:: example-reduce-pseudo-inverse
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_reduce_pseudo_inverse.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      Reduced:
      -3.000000, 1.500000, 0.000000
       3.000000, 1.500000, 6.000000
      Not implemented: FALSE

   .. image:: ../images/curve_reduce.png
      :align: center

.. c:function:: void BEZ_specialize_curve(const int *num_nodes, \
                                          const int *dimension, \
                                          const double *nodes, \
                                          const double *start, \
                                          const double *end, \
                                          double *new_nodes)

   Specialize a B |eacute| zier curve to an interval
   :math:`\left[a, b\right]`. This produces the control points
   for the curve given by :math:`B\left(a + (b - a) s\right)`.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param start:
      **[Input]** The start :math:`a` of the specialized interval.
   :type start: :c:expr:`const double*`
   :param end:
      **[Input]** The end :math:`b` of the specialized interval.
   :type end: :c:expr:`const double*`
   :param new_nodes:
      **[Output]** The control points of the specialized curve, as a
      :math:`D \times N` array, laid out in Fortran order.
   :type new_nodes: :c:expr:`double*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_specialize_curve(const int *num_nodes,
                           const int *dimension,
                           const double *nodes,
                           const double *start,
                           const double *end,
                           double *new_nodes);

   **Example:**

   When we specialize the curve :math:`B(s) = \left[\begin{array}{c} 0 \\ 0
   \end{array}\right] (1 - s)^2 + \frac{1}{2} \left[\begin{array}{c} 1 \\ 2
   \end{array}\right] 2 (1 - s) s + \left[\begin{array}{c} 1 \\ 0
   \end{array}\right] s^2 = \left[\begin{array}{c} s \\ 2s(1 - s)
   \end{array}\right]` to the interval :math:`\left[-\frac{1}{4},
   \frac{3}{4}\right]`:

   .. literalinclude:: example_specialize_curve.c
      :language: c
      :dedent: 4
      :lines: 18-31

   we get the specialized curve :math:`S(t) = \frac{1}{8} \left[
   \begin{array}{c} -2 \\ -5 \end{array}\right] (1 - s)^2 + \frac{1}{8}
   \left[\begin{array}{c} 2 \\ 7 \end{array}\right] 2 (1 - s) s + \frac{1}{8}
   \left[\begin{array}{c} 6 \\ 3 \end{array}\right] s^2 = \frac{1}{8}
   \left[\begin{array}{c} 2(4t - 1) \\ (4t - 1)(5 - 4t) \end{array}\right]`,
   which still lies on :math:`y = 2x(1 - x)`:

   .. testcode:: example-specialize-curve
      :hide:

      build_and_run_c("example_specialize_curve.c")

   .. testoutput:: example-specialize-curve
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_specialize_curve.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      New Nodes:
      -0.250000, 0.250000, 0.750000
      -0.625000, 0.875000, 0.375000

   .. image:: ../images/curve_specialize.png
      :align: center

.. c:function:: void BEZ_subdivide_nodes_curve(const int *num_nodes, \
                                               const int *dimension, \
                                               const double *nodes, \
                                               double *left_nodes, \
                                               double *right_nodes)

   Split a B |eacute| zier curve into two halves
   :math:`B\left(\left[0, \frac{1}{2}\right]\right)` and
   :math:`B\left(\left[\frac{1}{2}, 1\right]\right)`.

   :param num_nodes:
      **[Input]** The number of control points :math:`N` of a
      B |eacute| zier curve.
   :type num_nodes: :c:expr:`const int*`
   :param dimension:
      **[Input]** The dimension :math:`D` such that the curve lies in
      :math:`\mathbf{R}^D`.
   :type dimension: :c:expr:`const int*`
   :param nodes:
      **[Input]** The actual control points of the curve as a
      :math:`D \times N` array. This should be laid out in Fortran order,
      with :math:`D N` total values.
   :type nodes: :c:expr:`const double*`
   :param left_nodes:
      **[Output]** The control points of the left half curve
      :math:`B\left(\left[0, \frac{1}{2}\right]\right)` as a
      :math:`D \times N` array, laid out in Fortran order.
   :type left_nodes: :c:expr:`double*`
   :param right_nodes:
      **[Output]** The control points of the right half curve
      :math:`B\left(\left[\frac{1}{2}, 1\right]\right)` as a
      :math:`D \times N` array, laid out in Fortran order.
   :type right_nodes: :c:expr:`double*`

   **Signature:**

   .. code-block:: c

      void
      BEZ_subdivide_nodes_curve(const int *num_nodes,
                                const int *dimension,
                                const double *nodes,
                                double *left_nodes,
                                double *right_nodes);

   **Example:**

   For example, subdividing the curve :math:`B(s) = \left[\begin{array}{c}
   0 \\ 0 \end{array}\right] (1 - s)^2 + \frac{1}{4} \left[\begin{array}{c}
   5 \\ 12 \end{array}\right] 2 (1 - s) s + \left[\begin{array}{c} 2 \\ 1
   \end{array}\right] s^2`:

   .. literalinclude:: example_subdivide_nodes_curve.c
      :language: c
      :dedent: 4
      :lines: 18-34

   yields:

   .. testcode:: example-subdivide-nodes-curve
      :hide:

      build_and_run_c("example_subdivide_nodes_curve.c")

   .. testoutput:: example-subdivide-nodes-curve
      :options: +NORMALIZE_WHITESPACE
      :windows-skip:

      $ INCLUDE_DIR=.../libbezier-release/usr/include
      $ LIB_DIR=.../libbezier-release/usr/lib
      $ gcc \
      >     -o example \
      >     example_subdivide_nodes_curve.c \
      >     -I "${INCLUDE_DIR}" \
      >     -L "${LIB_DIR}" \
      >     -Wl,-rpath,"${LIB_DIR}" \
      >     -lbezier \
      >     -lm -lgfortran
      $ ./example
      Left Nodes:
      0.000000, 0.625000, 1.125000
      0.000000, 1.500000, 1.750000
      Right Nodes:
      1.125000, 1.625000, 2.000000
      1.750000, 2.000000, 1.000000

   .. image:: ../images/curve_subdivide.png
      :align: center