docs: def ringscan(x0, y0, r1, r2, metric=chebyshev):
      """Scan pixels in a ring pattern around a center point clockwise
  
      :param x0: Center x-coordinate
      :type x0: int
      :param y0: Center y-coordinate
      :type y0: int
      :param r1: Initial radius
      :type r1: int
      :param r2: Final radius
      :type r2: int
      :param metric: Distance metric
      :type metric: function
      :returns: Coordinate generator
      :rtype: function
      """
  
      # Validate inputs
      if r1 < 0: raise ValueError("Initial radius must be non-negative")
      if r2 < 0: raise ValueError("Final radius must be non-negative")
      if not hasattr(metric, "__call__"): raise TypeError("Metric not callable")
  
      # Define clockwise step directions
      direction = 0
      steps = {0: [ 1, 0],
               1: [ 1,-1],
               2: [ 0,-1],
               3: [-1,-1],
               4: [-1, 0],
               5: [-1, 1],
               6: [ 0, 1],
               7: [ 1, 1]}
      nsteps = len(steps)
  
      center = [x0, y0]
      
      # Scan distances outward (1) or inward (-1)
      rstep = 1 if r2 >= r1 else -1
      for distance in range(r1, r2 + rstep, rstep):
  
          initial = [x0, y0 + distance]
          current = initial
  
          # Number of tries to find a valid neighrbor
          ntrys = 0
  
          while True:
  
              # Short-circuit special case
              if distance == 0:
                  yield current[0], current[1]
                  break
  
              # Try and take a step and check if still within distance
              nextpoint = [current[i] + steps[direction][i] for i in range(2)]
              if metric(center, nextpoint) != distance:
  
                  # Check if we tried all step directions and failed
                  ntrys += 1
                  if ntrys == nsteps:
                      break
                  
                  # Try the next direction
                  direction = (direction + 1) % nsteps
                  continue
  
              ntrys = 0
              yield current[0], current[1]
  
              # Check if we have come all the way around
              current = nextpoint
              if current == initial:
                  break
  
          # Check if we tried all step directions and failed
          if ntrys == nsteps:
              break