code: def permutations(mesh,
                   function=lambda x: x.identifier,
                   displacement_max=1e-8,
                   count=1000,
                   subdivisions=2,
                   cutoff=3600):
      
  
      identifiers = []
      start = time.time()
  
      # do subdivisions
      divided = [mesh.copy()]
      for j in range(subdivisions - 1):
          divided.append(divided[-1].copy().subdivide())
  
      for i, displacement in enumerate(np.linspace(0.0,
                                                   displacement_max / mesh.scale,
                                                   count)):
          # get one of the subdivided meshes
          current = np.random.choice(divided).copy()
  
          if i > (count / 10):
              # run first bunch without tessellation permutation
              current = current.permutate.tessellation()
              # after the first few displace it a lot
  
          transformed = trimesh.permutate.transform(current)
          # noisy = trimesh.permutate.noise(transformed, displacement)
  
          identifier = function(transformed)
          identifiers.append(identifier)
  
          if (time.time() - start) > cutoff:
              print('bailing for time:{} count:{}'.format(time.time() - start,
                                                          i))
              return np.array(identifiers)
  
      return np.array(identifiers)
docs: Permutate a mesh, record the maximum it deviates from the original mesh
      and the resulting value of an identifier function.
  
      Parameters
      ----------
      mesh:     Trimesh object
      function:     function which takes a single mesh as an argument
                    and returns an (n,) float vector
      subdivisions: the maximum number of times to subdivide the mesh
      count: int, number of times to permutate each subdivision step
  
      Returns
      -----------
      identifiers: numpy array of identifiers