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
def _call_function_under_test(vec0, vec1): from bezier.hazmat import helpers return helpers.cross_product(vec0, vec1)
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