docs: def _compute_projection_pick(artist, path, xy):
      """
      Project *xy* on *path* to obtain a `Selection` for *artist*.
  
      *path* is first transformed to screen coordinates using the artist
      transform, and the target of the returned `Selection` is transformed
      back to data coordinates using the artist *axes* inverse transform.  The
      `Selection` `index` is returned as a float.  This function returns ``None``
      for degenerate inputs.
  
      The caller is responsible for converting the index to the proper class if
      needed.
      """
      transform = artist.get_transform().frozen()
      tpath = (path.cleaned(transform) if transform.is_affine
               # `cleaned` only handles affine transforms.
               else transform.transform_path(path).cleaned())
      # `cleaned` should return a path where the first element is `MOVETO`, the
      # following are `LINETO` or `CLOSEPOLY`, and the last one is `STOP`, i.e.
      #     codes = path.codes
      #     assert (codes[0], codes[-1]) == (path.MOVETO, path.STOP)
      #     assert np.in1d(codes[1:-1], [path.LINETO, path.CLOSEPOLY]).all()
      vertices = tpath.vertices[:-1]
      codes = tpath.codes[:-1]
      vertices[codes == tpath.CLOSEPOLY] = vertices[0]
      # Unit vectors for each segment.
      us = vertices[1:] - vertices[:-1]
      ls = np.hypot(*us.T)
      with np.errstate(invalid="ignore"):
          # Results in 0/0 for repeated consecutive points.
          us /= ls[:, None]
      # Vectors from each vertex to the event (overwritten below).
      vs = xy - vertices[:-1]
      # Clipped dot products -- `einsum` cannot be done in place, `clip` can.
      # `clip` can trigger invalid comparisons if there are nan points.
      with np.errstate(invalid="ignore"):
          dot = np.clip(np.einsum("ij,ij->i", vs, us), 0, ls, out=vs[:, 0])
      # Projections.
      projs = vertices[:-1] + dot[:, None] * us
      ds = np.hypot(*(xy - projs).T, out=vs[:, 1])
      try:
          argmin = np.nanargmin(ds)
          dmin = ds[argmin]
      except (ValueError, IndexError):  # See above re: exceptions caught.
          return
      else:
          target = AttrArray(
              artist.axes.transData.inverted().transform_point(projs[argmin]))
          target.index = (
              (argmin + dot[argmin] / ls[argmin])
              / (path._interpolation_steps / tpath._interpolation_steps))
          return Selection(artist, target, dmin, None, None)