docs: def retino_colors(vcolorfn, *args, **kwargs):
      'See eccen_colors, angle_colors, sigma_colors, and varea_colors.'
      if len(args) == 0:
          def _retino_color_pass(*args, **new_kwargs):
              return retino_colors(vcolorfn, *args,
                                   **{k:(new_kwargs[k] if k in new_kwargs else kwargs[k])
                                      for k in set(kwargs.keys() + new_kwargs.keys())})
          return _retino_color_pass
      elif len(args) > 1:
          raise ValueError('retinotopy color functions accepts at most one argument')
      m = args[0]
      # we need to handle the arguments
      if isinstance(m, (geo.VertexSet, pimms.ITable)):
          tbl = m.properties if isinstance(m, geo.VertexSet) else m
          n = tbl.row_count
          # if the weight or property arguments are lists, we need to thread these along
          if 'property' in kwargs:
              props = kwargs['property']
              del kwargs['property']
              if not (pimms.is_vector(props) or pimms.is_matrix(props)):
                  props = [props for _ in range(n)]
          else: props = None
          if 'weight' in kwargs:
              ws = kwargs['weight']
              del kwargs['weight']
              if not pimms.is_vector(ws) and not pimms.is_matrix(ws): ws = [ws for _ in range(n)]
          else: ws = None
          vcolorfn0 = vcolorfn(Ellipsis, **kwargs) if len(kwargs) > 0 else vcolorfn
          if props is None and ws is None: vcfn = lambda m,k:vcolorfn0(m)
          elif props is None:              vcfn = lambda m,k:vcolorfn0(m, weight=ws[k])
          elif ws is None:                 vcfn = lambda m,k:vcolorfn0(m, property=props[k])
          else: vcfn = lambda m,k:vcolorfn0(m, property=props[k], weight=ws[k])
          return np.asarray([vcfn(r,kk) for (kk,r) in enumerate(tbl.rows)])
      else:
          return vcolorfn(m, **kwargs)