示例#1
0
def locate_point(curve, point):
    r"""Locate a point on a curve.

    Does so by recursively subdividing the curve and rejecting
    sub-curves with bounding boxes that don't contain the point.
    After the sub-curves are sufficiently small, uses Newton's
    method to zoom in on the parameter value.

    .. note::

       This assumes, but does not check, that ``point`` is ``1xD``,
       where ``D`` is the dimension that ``curve`` is in.

    Args:
        curve (.Curve): A B |eacute| zier curve.
        point (numpy.ndarray): The point to locate.

    Returns:
        Optional[float]: The parameter value (:math:`s`) corresponding
        to ``point`` or :data:`None` if the point is not on the ``curve``.

    Raises:
        ValueError: If the standard deviation of the remaining start / end
            parameters among the subdivided intervals exceeds a given
            threshold (e.g. :math:`2^{-20}`).
    """
    candidates = [curve]
    for _ in six.moves.xrange(_MAX_LOCATE_SUBDIVISIONS + 1):
        next_candidates = []
        for candidate in candidates:
            nodes = candidate._nodes  # pylint: disable=protected-access
            if _helpers.contains_nd(nodes, point):
                next_candidates.extend(candidate.subdivide())

        candidates = next_candidates

    if not candidates:
        return None

    # pylint: disable=protected-access
    params = [
        (candidate._start, candidate._end) for candidate in candidates]
    # pylint: enable=protected-access

    if np.std(params) > _LOCATE_STD_CAP:
        raise ValueError(
            'Parameters not close enough to one another', params)

    s_approx = np.mean(params)
    return newton_refine(curve, point, s_approx)
示例#2
0
def update_locate_candidates(candidate, next_candidates, x_val, y_val, degree):
    """Update list of candidate surfaces during geometric search for a point.

    .. note::

       This is used **only** as a helper for :func:`locate_point`.

    Checks if the point ``(x_val, y_val)`` is contained in the ``candidate``
    surface. If not, this function does nothing. If the point is contaned,
    the four subdivided surfaces from ``candidate`` are added to
    ``next_candidates``.

    Args:
        candidate (Tuple[float, float, float, numpy.ndarray]): A 4-tuple
            describing a surface and its centroid / width. Contains

            * Three times centroid ``x``-value
            * Three times centroid ``y``-value
            * "Width" of parameter space for the surface
            * Control points for the surface
        next_candidates (list): List of "candidate" sub-surfaces that may
            contain the point being located.
        x_val (float): The ``x``-coordinate being located.
        y_val (float): The ``y``-coordinate being located.
        degree (int): The degree of the surface.
    """
    centroid_x, centroid_y, width, candidate_nodes = candidate
    point = np.asfortranarray([x_val, y_val])
    if not _helpers.contains_nd(candidate_nodes, point):
        return

    nodes_a, nodes_b, nodes_c, nodes_d = _surface_helpers.subdivide_nodes(
        candidate_nodes, degree)
    half_width = 0.5 * width
    next_candidates.extend((
        (
            centroid_x - half_width,
            centroid_y - half_width,
            half_width,
            nodes_a,
        ),
        (centroid_x, centroid_y, -half_width, nodes_b),
        (centroid_x + width, centroid_y - half_width, half_width, nodes_c),
        (centroid_x - half_width, centroid_y + width, half_width, nodes_d),
    ))
示例#3
0
def _locate_point(nodes, point):
    r"""Locate a point on a curve.

    Does so by recursively subdividing the curve and rejecting
    sub-curves with bounding boxes that don't contain the point.
    After the sub-curves are sufficiently small, uses Newton's
    method to zoom in on the parameter value.

    .. note::

       This assumes, but does not check, that ``point`` is ``D x 1``,
       where ``D`` is the dimension that ``curve`` is in.

    .. 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 defining a B |eacute| zier curve.
        point (numpy.ndarray): The point to locate.

    Returns:
        Optional[float]: The parameter value (:math:`s`) corresponding
        to ``point`` or :data:`None` if the point is not on the ``curve``.

    Raises:
        ValueError: If the standard deviation of the remaining start / end
            parameters among the subdivided intervals exceeds a given
            threshold (e.g. :math:`2^{-20}`).
    """
    candidates = [(0.0, 1.0, nodes)]
    for _ in six.moves.xrange(_MAX_LOCATE_SUBDIVISIONS + 1):
        next_candidates = []
        for start, end, candidate in candidates:
            if _helpers.contains_nd(candidate, point.ravel(order="F")):
                midpoint = 0.5 * (start + end)
                left, right = subdivide_nodes(candidate)
                next_candidates.extend(
                    ((start, midpoint, left), (midpoint, end, right))
                )
        candidates = next_candidates
    if not candidates:
        return None

    params = [(start, end) for start, end, _ in candidates]
    if np.std(params) > _LOCATE_STD_CAP:
        raise ValueError("Parameters not close enough to one another", params)

    s_approx = np.mean(params)
    s_approx = newton_refine(nodes, point, s_approx)
    # NOTE: Since ``np.mean(params)`` must be in ``[0, 1]`` it's
    #       "safe" to push the Newton-refined value back into the unit
    #       interval.
    if s_approx < 0.0:
        return 0.0

    elif s_approx > 1.0:
        return 1.0

    else:
        return s_approx
示例#4
0
    def _call_function_under_test(nodes, point):
        from bezier import _helpers

        return _helpers.contains_nd(nodes, point)