docs: def circlescan(x0, y0, r1, r2):
      """Scan pixels in a circle pattern around a center point
  
      :param x0: Center x-coordinate
      :type x0: float
      :param y0: Center y-coordinate
      :type y0: float
      :param r1: Initial radius
      :type r1: float
      :param r2: Final radius
      :type r2: float
      :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")
  
      # List of pixels visited in previous diameter
      previous = []
      
      # Scan distances outward (1) or inward (-1)
      rstep = 1 if r2 >= r1 else -1
      for distance in range(r1, r2 + rstep, rstep):
  
          if distance == 0:
  
              yield x0, y0
  
          else:
  
              # Computes points for first octant and the rotate by multiples of
              # 45 degrees to compute the other octants
              a = 0.707107
              rotations = {0: [[ 1, 0], [ 0, 1]],
                           1: [[ a, a], [-a, a]],
                           2: [[ 0, 1], [-1, 0]],
                           3: [[-a, a], [-a,-a]],
                           4: [[-1, 0], [ 0,-1]],
                           5: [[-a,-a], [ a,-a]],
                           6: [[ 0,-1], [ 1, 0]],
                           7: [[ a,-a], [ a, a]]}
              nangles = len(rotations)
  
              # List of pixels visited in current diameter
              current = []
  
              for angle in range(nangles):
                  x = 0
                  y = distance
                  d = 1 - distance
                  while x < y:
                      xr = rotations[angle][0][0]*x + rotations[angle][0][1]*y
                      yr = rotations[angle][1][0]*x + rotations[angle][1][1]*y
                      xr = x0 + xr
                      yr = y0 + yr
                      
                      # First check  if point was in previous diameter
                      # since our scan pattern can lead to duplicates in
                      # neighboring diameters
                      point = (int(round(xr)), int(round(yr)))
                      if point not in previous:
                          yield xr, yr
                          current.append(point)
  
                      # Move pixel according to circle constraint
                      if (d < 0):
                          d += 3 + 2 * x
                      else:
                          d += 5 - 2 * (y-x)
                          y -= 1
                      x += 1
  
              previous = current