Return an array of vertex indexes representing the convex hull.

        If faces have not been computed for this mesh, the function
        computes them.
        If no vertices or faces are specified, the function returns None.
161035
def convex_hull(self):
        
        if self._faces is None:
            if self._vertices is None:
                return None
            self.triangulate()
        return self._convex_hull
553310
def indexes(self):
        
        if self._indexes is None:
            self._indexes = default_indexes(self._coords, self.dims)
        return Indexes(self._indexes)
673454
def _arrayize_vectorized_indexer(indexer, shape):
    
    slices = [v for v in indexer.tuple if isinstance(v, slice)]
    if len(slices) == 0:
        return indexer

    arrays = [v for v in indexer.tuple if isinstance(v, np.ndarray)]
    n_dim = arrays[0].ndim if len(arrays) > 0 else 0
    i_dim = 0
    new_key = []
    for v, size in zip(indexer.tuple, shape):
        if isinstance(v, np.ndarray):
            new_key.append(np.reshape(v, v.shape + (1, ) * len(slices)))
        else:  # slice
            shape = ((1,) * (n_dim + i_dim) + (-1,) +
                     (1,) * (len(slices) - i_dim - 1))
            new_key.append(np.arange(*v.indices(size)).reshape(shape))
            i_dim += 1
    return VectorizedIndexer(tuple(new_key))
1078335
def get_closest_points(self, mesh):
        
        # first, for each point in mesh compute minimum distance to each
        # surface. The distance matrix is flattend, because mesh can be of
        # an arbitrary shape. By flattening we obtain a ``distances`` matrix
        # for which the first dimension represents the different surfaces
        # and the second dimension the mesh points.
        dists = numpy.array(
            [surf.get_min_distance(mesh).flatten() for surf in self.surfaces]
        )

        # find for each point in mesh the index of closest surface
        idx = dists == numpy.min(dists, axis=0)

        # loop again over surfaces. For each surface compute the closest
        # points, and associate them to the mesh points for which the surface
        # is the closest. Note that if a surface is not the closest to any of
        # the mesh points then the calculation is skipped
        lons = numpy.empty_like(mesh.lons.flatten())
        lats = numpy.empty_like(mesh.lats.flatten())
        depths = None if mesh.depths is None else \
            numpy.empty_like(mesh.depths.flatten())
        for i, surf in enumerate(self.surfaces):
            if not idx[i, :].any():
                continue
            cps = surf.get_closest_points(mesh)
            lons[idx[i, :]] = cps.lons.flatten()[idx[i, :]]
            lats[idx[i, :]] = cps.lats.flatten()[idx[i, :]]
            if depths is not None:
                depths[idx[i, :]] = cps.depths.flatten()[idx[i, :]]
        lons = lons.reshape(mesh.lons.shape)
        lats = lats.reshape(mesh.lats.shape)
        if depths is not None:
            depths = depths.reshape(mesh.depths.shape)

        return Mesh(lons, lats, depths)
473519
def edges
      return_edges = Array.new(3)
      return_edges[0] = FiniteLine.new(self.vertices[0], self.vertices[1])
      return_edges[1] = FiniteLine.new(self.vertices[1], self.vertices[2])
      return_edges[2] = FiniteLine.new(self.vertices[2], self.vertices[0])
      return return_edges
    end
74966
def _get_index(self):
        
        contour = self.contour
        if contour is None:
            return None
        return contour.points.index(self)
1049674
def set_faces(self, faces):
        
        self._faces = faces
        self._edges = None
        self._edges_indexed_by_faces = None
        self._vertex_faces = None
        self._vertices_indexed_by_faces = None
        self.reset_normals()
        self._vertex_colors_indexed_by_faces = None
        self._face_colors_indexed_by_faces = None
1129262
def identify_vertex_neighbours(self, vertex):
        
        simplices = self.simplices
        ridx, cidx = np.where(simplices == vertex)
        neighbour_array = np.unique(np.hstack([simplices[ridx]])).tolist()
        neighbour_array.remove(vertex)
        return neighbour_array
538952
def keep_vertices(self, indices_to_keep, ret_kept_faces=False):
        '''
        

        '''
        import numpy as np

        if self.v is None:
            return

        indices_to_keep = np.array(indices_to_keep, dtype=np.uint32)

        initial_num_verts = self.v.shape[0]
        if self.f is not None:
            initial_num_faces = self.f.shape[0]
            f_indices_to_keep = self.all_faces_with_verts(indices_to_keep, as_boolean=True)

        # Why do we test this? Don't know. But we do need to test it before we
        # mutate self.v.
        vn_should_update = self.vn is not None and self.vn.shape[0] == initial_num_verts
        vc_should_update = self.vc is not None and self.vc.shape[0] == initial_num_verts

        self.v = self.v[indices_to_keep]

        if vn_should_update:
            self.vn = self.vn[indices_to_keep]
        if vc_should_update:
            self.vc = self.vc[indices_to_keep]

        if self.f is not None:
            v_old_to_new = np.zeros(initial_num_verts, dtype=int)
            f_old_to_new = np.zeros(initial_num_faces, dtype=int)

            v_old_to_new[indices_to_keep] = np.arange(len(indices_to_keep), dtype=int)
            self.f = v_old_to_new[self.f[f_indices_to_keep]]
            f_old_to_new[f_indices_to_keep] = np.arange(self.f.shape[0], dtype=int)

        else:
            # Make the code below work, in case there is somehow degenerate
            # segm even though there are no faces.
            f_indices_to_keep = []

        if self.segm is not None:
            new_segm = {}
            for segm_name, segm_faces in self.segm.items():
                faces = np.array(segm_faces, dtype=int)
                valid_faces = faces[f_indices_to_keep[faces]]
                if len(valid_faces):
                    new_segm[segm_name] = f_old_to_new[valid_faces]
            self.segm = new_segm if new_segm else None

        if hasattr(self, '_raw_landmarks') and self._raw_landmarks is not None:
            self.recompute_landmarks()

        return np.nonzero(f_indices_to_keep)[0] if ret_kept_faces else self
1050253
def faces(self, sites, permutation=None):
        
        if permutation is None:
            coords = [site.coords for site in sites]
        else:
            coords = [sites[ii].coords for ii in permutation]
        return [[coords[ii] for ii in f] for f in self._faces]
1036156
def referenced_vertices(self):
        
        # no entities no reference
        if len(self.entities) == 0:
            return np.array([], dtype=np.int64)
        referenced = np.concatenate([e.points for e in self.entities])
        referenced = np.unique(referenced.astype(np.int64))

        return referenced
214439
def hull(self):
        
        from scipy.spatial import ConvexHull
        if len(self.coordinates) >= 4:
            inds = ConvexHull(self.coordinates).vertices
            return self.coordinates[inds]
        else:
            return self.coordinates
180462
function (options) {
  options = options || {}
  if (('points' in options) !== ('faces' in options)) {
    throw new Error("polyhedron needs 'points' and 'faces' arrays")
  }
  let vertices = parseOptionAs3DVectorList(options, 'points', [
            [1, 1, 0],
            [1, -1, 0],
            [-1, -1, 0],
            [-1, 1, 0],
            [0, 0, 1]
  ])
        .map(function (pt) {
          return new Vertex3(pt)
        })
  let faces = parseOption(options, 'faces', [
            [0, 1, 4],
            [1, 2, 4],
            [2, 3, 4],
            [3, 0, 4],
            [1, 0, 3],
            [2, 1, 3]
  ])
    // Openscad convention defines inward normals - so we have to invert here
  faces.forEach(function (face) {
    face.reverse()
  })
  let polygons = faces.map(function (face) {
    return new Polygon3(face.map(function (idx) {
      return vertices[idx]
    }))
  })

    // TODO: facecenters as connectors? probably overkill. Maybe centroid
    // the re-tesselation here happens because it's so easy for a user to
    // create parametrized polyhedrons that end up with 1-2 dimensional polygons.
    // These will create infinite loops at CSG.Tree()
  return fromPolygons(polygons).reTesselated()
}
1274767
def from_indexed_arrays(cls, name, verts, normals):
        """"""

        # Put header in string
        wavefront_str = "o {name}\n".format(name=name)


        new_verts, face_indices = face_index(verts)
        assert new_verts.shape[1] == 3, "verts should be Nx3 array"
        assert face_indices.ndim == 2

        face_indices = fan_triangulate(face_indices)

        # Write Vertex data from vert_dict
        for vert in new_verts:
            wavefront_str += "v {0} {1} {2}\n".format(*vert)

        # Write (false) UV Texture data
        wavefront_str += "vt 1.0 1.0\n"

        for norm in normals:
            wavefront_str += "vn {0} {1} {2}\n".format(*norm)

        assert len(face_indices) == len(normals) * 2
        for norm_idx, vert_idx,  in enumerate(face_indices):
            wavefront_str += "f"
            for vv in vert_idx:
                wavefront_str += " {}/{}/{}".format(vv + 1, 1, (norm_idx // 2) + 1 )
            wavefront_str += "\n"

        return cls(string=wavefront_str)
111299
def mesh(self):
        """"""
        triangles = np.empty((self.f.shape[0], 4))
        triangles[:, -3:] = self.f
        triangles[:, 0] = 3
        return vtki.PolyData(self.v, triangles, deep=False)
83718
def vertices_per_edge(self):
        """"""
        import numpy as np
        return np.asarray([vertices_in_common(e[0], e[1]) for e in self.f[self.faces_per_edge]])
986146
def convex_conj(self):
        
        if self.scalar <= 0:
            raise ValueError('scaling with nonpositive values have no convex '
                             'conjugate. Current value: {}.'
                             ''.format(self.scalar))

        return self.scalar * self.functional.convex_conj * (1.0 / self.scalar)