def __call__(self, s, t):
        r"""This computes :math:`DG^T G` and :math:`DG^T DG`.

        If :math:`DG^T DG` is not full rank, this means either :math:`DG`
        was not full rank or that it was, but with a relatively high condition
        number. So, in the case that :math:`DG^T DG` is singular, the
        assumption is that the intersection has a multiplicity higher than two.

        Args:
            s (float): The parameter where we'll compute :math:`G(s, t)` and
                :math:`DG(s, t)`.
            t (float): The parameter where we'll compute :math:`G(s, t)` and
                :math:`DG(s, t)`.

        Returns:
            Tuple[Optional[numpy.ndarray], Optional[numpy.ndarray]]: Pair of

            * The LHS matrix ``DG^T DG``, a ``2 x 2`` array. If ``G == 0`` then
              this matrix won't be computed and :data:`None` will be returned.
            * The RHS vector ``DG^T G``, a ``2 x 1`` array.
        """
        s_vals = np.asfortranarray([s])
        b1_s = _py_curve_helpers.evaluate_multi(self.nodes1, s_vals)
        b1_ds = _py_curve_helpers.evaluate_multi(self.first_deriv1, s_vals)
        t_vals = np.asfortranarray([t])
        b2_t = _py_curve_helpers.evaluate_multi(self.nodes2, t_vals)
        b2_dt = _py_curve_helpers.evaluate_multi(self.first_deriv2, t_vals)
        func_val = np.empty((3, 1), order="F")
        func_val[:2, :] = b1_s - b2_t
        func_val[2, :] = _py_helpers.cross_product(b1_ds[:, 0], b2_dt[:, 0])
        if np.all(func_val == 0.0):
            return None, func_val[:2, :]

        else:
            jacobian = np.empty((3, 2), order="F")
            jacobian[:2, :1] = b1_ds
            jacobian[:2, 1:] = -b2_dt
            if self.second_deriv1.size == 0:
                jacobian[2, 0] = 0.0
            else:
                jacobian[2, 0] = _py_helpers.cross_product(
                    _py_curve_helpers.evaluate_multi(
                        self.second_deriv1, s_vals
                    )[:, 0],
                    b2_dt[:, 0],
                )
            if self.second_deriv2.size == 0:
                jacobian[2, 1] = 0.0
            else:
                jacobian[2, 1] = _py_helpers.cross_product(
                    b1_ds[:, 0],
                    _py_curve_helpers.evaluate_multi(
                        self.second_deriv2, t_vals
                    )[:, 0],
                )
            modified_lhs = _py_helpers.matrix_product(jacobian.T, jacobian)
            modified_rhs = _py_helpers.matrix_product(jacobian.T, func_val)
            return modified_lhs, modified_rhs
Example #2
0
    def _call_function_under_test(vec0, vec1):
        from bezier.hazmat import helpers

        return helpers.cross_product(vec0, vec1)
Example #3
0
def get_curvature(nodes, tangent_vec, s):
    r"""Compute the signed curvature of a curve at :math:`s`.

    Computed via

    .. math::

       \frac{B'(s) \times B''(s)}{\left\lVert B'(s) \right\rVert_2^3}

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

    .. testsetup:: get-curvature

       import numpy as np
       import bezier
       from bezier.hazmat.curve_helpers import evaluate_hodograph
       from bezier.hazmat.curve_helpers import get_curvature

    .. doctest:: get-curvature
       :options: +NORMALIZE_WHITESPACE

       >>> import numpy as np
       >>> nodes = np.asfortranarray([
       ...     [1.0, 0.75,  0.5, 0.25, 0.0],
       ...     [0.0, 2.0 , -2.0, 2.0 , 0.0],
       ... ])
       >>> s = 0.5
       >>> tangent_vec = evaluate_hodograph(s, nodes)
       >>> tangent_vec
       array([[-1.],
              [ 0.]])
       >>> curvature = get_curvature(nodes, tangent_vec, s)
       >>> curvature
       -12.0

    .. testcleanup:: get-curvature

       import make_images
       make_images.get_curvature(nodes, s, tangent_vec, curvature)

    .. note::

       There is also a Fortran implementation of this function, which
       will be used if it can be built.

    Args:
        nodes (numpy.ndarray): The nodes of a curve.
        tangent_vec (numpy.ndarray): The already computed value of
            :math:`B'(s)`
        s (float): The parameter value along the curve.

    Returns:
        float: The signed curvature.
    """
    _, num_nodes = np.shape(nodes)
    if num_nodes == 2:  # Lines have no curvature.
        return 0.0

    # NOTE: We somewhat replicate code in ``evaluate_hodograph()`` here.
    first_deriv = nodes[:, 1:] - nodes[:, :-1]
    second_deriv = first_deriv[:, 1:] - first_deriv[:, :-1]
    concavity = (
        (num_nodes - 1)
        * (num_nodes - 2)
        * evaluate_multi(second_deriv, np.asfortranarray([s]))
    )
    curvature = _py_helpers.cross_product(
        tangent_vec.ravel(order="F"), concavity.ravel(order="F")
    )
    # NOTE: We convert to 1D to make sure NumPy uses vector norm.
    curvature /= np.linalg.norm(tangent_vec[:, 0], ord=2) ** 3
    return curvature