Exemple #1
0
def Disc(center=(0., 0., 0.),
         inner=0.25,
         outer=0.5,
         normal=(0, 0, 1),
         r_res=1,
         c_res=6):
    """Create a polygonal disk with a hole in the center.

    The disk has zero height. The user can specify the inner and outer radius
    of the disk, and the radial and circumferential resolution of the polygonal
    representation.

    Parameters
    ----------
    center : np.ndarray or list
        Center in [x, y, z]. middle of the axis of the disc.

    inner : float
        The inner radius

    outer : float
        The outer radius

    normal : np.ndarray or list
        direction vector in [x, y, z]. orientation vector of the cone.

    r_res: int
        number of points in radius direction.

    r_res: int
        number of points in circumferential direction.

    """
    src = vtk.vtkDiskSource()
    src.SetInnerRadius(inner)
    src.SetOuterRadius(outer)
    src.SetRadialResolution(r_res)
    src.SetCircumferentialResolution(c_res)
    src.Update()
    return pyvista.wrap(src.GetOutput())
Exemple #2
0
def point_set_to_vtk(pse):
    """Convert the point set to a :class:`pyvista.PolyData` data object.

    Args:
        pse (:class:`omf.pointset.PointSetElement`): The point set to convert

    Return:
        :class:`pyvista.PolyData`
    """

    points = pse.geometry.vertices
    npoints = pse.geometry.num_nodes

    # Make VTK cells array
    cells = np.hstack((np.ones(
        (npoints, 1)), np.arange(npoints).reshape(-1, 1)))
    cells = np.ascontiguousarray(cells, dtype=np.int64)
    cells = np.reshape(cells, (2 * npoints))
    vtkcells = vtk.vtkCellArray()
    vtkcells.SetCells(
        npoints, nps.numpy_to_vtk(cells, deep=True,
                                  array_type=vtk.VTK_ID_TYPE))

    # Convert points to vtk object
    pts = vtk.vtkPoints()
    pts.SetNumberOfPoints(pse.geometry.num_nodes)
    pts.SetData(nps.numpy_to_vtk(points))

    # Create polydata
    output = vtk.vtkPolyData()
    output.SetPoints(pts)
    output.SetVerts(vtkcells)

    # Now add point data:
    add_data(output, pse.data)

    add_textures(output, pse.textures, pse.name)

    return pyvista.wrap(output)
Exemple #3
0
def get_point_array(poly,l_dim=None):
	""" get back point array = shape(nb_cells, connnectivity, 3) #output will be transformed to zyx format
	if l_dim is filled in, the coordinates get transformed into pixel coordinates (eg l_dim = [1e-6,0.129e-6,0.129e-6])
	these coordinates are indexes, because flooring is applied (e.g. all pixels with z-location between 0 and 1e-6 will get z=0)
	"""
	try:
		pvw = pv.wrap(poly)
	except:
		pvw = poly
	
	conn = pvw.extract_cells(0).n_points
	a_points = np.zeros((pvw.n_cells,conn,3))
	for i in range(pvw.n_cells):
		points_cell= pvw.extract_cells(i).points
		for j in range(conn):
			a_points[i,j,:] = points_cell[j]
	
	a_points = a_points[:,:,[2,1,0]] # from xyz to zyx order
	if l_dim:
		a_points= np.floor(a_points / np.array(l_dim)).astype(int)  #turns metric coordinates into pixel coordinates

	return a_points
Exemple #4
0
def wrap_image_array(arr):
    """Wrap a numpy array as a pyvista.UniformGrid.

    Parameters
    ----------
    arr : np.ndarray
        A ``np.uint8`` ``(X, Y, (3 or 4)`` array.  For example
        ``(768, 1024, 3)``.

    """
    if arr.ndim != 3:
        raise ValueError('Expecting a X by Y by (3 or 4) array')
    if arr.shape[2] not in [3, 4]:
        raise ValueError('Expecting a X by Y by (3 or 4) array')
    if arr.dtype != np.uint8:
        raise ValueError('Expecting a np.uint8 array')

    img = _vtk.vtkImageData()
    img.SetDimensions(arr.shape[1], arr.shape[0], 1)
    wrap_img = pyvista.wrap(img)
    wrap_img.point_arrays['PNGImage'] = arr[::-1].reshape(-1, arr.shape[2])
    return wrap_img
Exemple #5
0
def vCutter(input_data, cut_function):
    """Returns the intersection of input_data and cut_function

    Wrapper around vtkCutter filter. Output contains interpolated data from
    input_data to the intersection location. Note that cell data is NOT passed
    through.

    Parameters
    ----------
    input_data : pyvista.PointSet
        Data that will be cut by the cut_function. Intersected point will have
        data interpolated.
    cut_function : vtk.vtkImplicitFunction
        VTK function that cuts the input_data. Most common example would be
        vtkPlane.
    """

    cutter = vtk.vtkCutter()
    cutter.SetCutFunction(cut_function)
    cutter.SetInputData(input_data)
    cutter.Update()
    return pv.wrap(cutter.GetOutput())
def Polygon(center=(0., 0., 0.), radius=1, normal=(0, 0, 1), n_sides=6):
    """Create a polygon.

    Parameters
    ----------
    center : iterable, optional
        Center in ``[x, y, z]``. Central axis of the polygon passes
        through this point.

    radius : float, optional
        The radius of the polygon.

    normal : iterable, optional
        Direction vector in ``[x, y, z]``. Orientation vector of the polygon.

    n_sides : int, optional
        Number of sides of the polygon.

    Returns
    -------
    pyvista.PolyData
        Mesh of the polygon.

    Examples
    --------
    Create an 8 sided polygon.

    >>> import pyvista
    >>> mesh = pyvista.Polygon(n_sides=8)
    >>> mesh.plot(show_edges=True, line_width=5)

    """
    src = _vtk.vtkRegularPolygonSource()
    src.SetCenter(center)
    src.SetNumberOfSides(n_sides)
    src.SetRadius(radius)
    src.SetNormal(normal)
    src.Update()
    return pyvista.wrap(src.GetOutput())
Exemple #7
0
def standard_reader_routine(reader, filename, attrs=None):
    """Use a given reader in the common VTK reading pipeline routine.

    The reader must come from the ``READERS`` mapping.

    Parameters
    ----------
    reader : vtkReader
        Any instantiated VTK reader class

    filename : str
        The string filename to the data file to read.

    attrs : dict, optional
        A dictionary of attributes to call on the reader. Keys of dictionary are
        the attribute/method names and values are the arguments passed to those
        calls. If you do not have any attributes to call, pass ``None`` as the
        value.

    """
    if attrs is None:
        attrs = {}
    if not isinstance(attrs, dict):
        raise TypeError(
            'Attributes must be a dictionary of name and arguments.')
    if filename is not None:
        reader.SetFileName(filename)
    # Apply any attributes listed
    for name, args in attrs.items():
        attr = getattr(reader, name)
        if args is not None:
            if not isinstance(args, (list, tuple)):
                args = [args]
            attr(*args)
        else:
            attr()
    # Perform the read
    reader.Update()
    return pyvista.wrap(reader.GetOutputDataObject(0))
Exemple #8
0
def Cube(
        center=(0., 0., 0.), x_length=1.0, y_length=1.0, z_length=1.0,
        bounds=None):
    """Create a cube by either specifying the center and side lengths or just
    the bounds of the cube. If ``bounds`` are given, all other arguments are
    ignored.

    Parameters
    ----------
    center : np.ndarray or list
        Center in [x, y, z].

    x_length : float
        length of the cube in the x-direction.

    y_length : float
        length of the cube in the y-direction.

    z_length : float
        length of the cube in the z-direction.

    bounds : np.ndarray or list
        Specify the bounding box of the cube. If given, all other arguments are
        ignored. ``(xMin,xMax, yMin,yMax, zMin,zMax)``
    """
    src = vtk.vtkCubeSource()
    if bounds is not None:
        if np.array(bounds).size != 6:
            raise TypeError(
                'Bounds must be given as length 6 tuple: (xMin,xMax, yMin,yMax, zMin,zMax)'
            )
        src.SetBounds(bounds)
    else:
        src.SetCenter(center)
        src.SetXLength(x_length)
        src.SetYLength(y_length)
        src.SetZLength(z_length)
    src.Update()
    return pyvista.wrap(src.GetOutput())
Exemple #9
0
def voxelize(mesh, density=None, check_surface=True):
    """Voxelize mesh to UnstructuredGrid.

    Parameters
    ----------
    density : float
        The uniform size of the voxels. Defaults to 1/100th of the mesh length.

    check_surface : bool
        Specify whether to check the surface for closure. If on, then the
        algorithm first checks to see if the surface is closed and
        manifold. If the surface is not closed and manifold, a runtime
        error is raised.

    """
    if not pyvista.is_pyvista_dataset(mesh):
        mesh = pyvista.wrap(mesh)
    if density is None:
        density = mesh.length / 100
    x_min, x_max, y_min, y_max, z_min, z_max = mesh.bounds
    x = np.arange(x_min, x_max, density)
    y = np.arange(y_min, y_max, density)
    z = np.arange(z_min, z_max, density)
    x, y, z = np.meshgrid(x, y, z)

    # Create unstructured grid from the structured grid
    grid = pyvista.StructuredGrid(x, y, z)
    ugrid = pyvista.UnstructuredGrid(grid)

    # get part of the mesh within the mesh's bounding surface.
    selection = ugrid.select_enclosed_points(mesh.extract_surface(),
                                             tolerance=0.0,
                                             check_surface=check_surface)
    mask = selection.point_arrays['SelectedPoints'].view(np.bool)

    # extract cells from point indices
    return ugrid.extract_points(mask)
Exemple #10
0
    def view_frustum(self, aspect=1.0):
        """Get the view frustum.

        Parameters
        ----------
        aspect : float, optional
            The aspect of the viewport to compute the planes. Defaults
            to 1.0.

        Returns
        -------
        frustum : pv.PolyData
            View frustum.

        Examples
        --------
        >>> import pyvista
        >>> plotter = pyvista.Plotter()
        >>> frustum = plotter.camera.view_frustum(1.0)
        >>> frustum.n_points
        8
        >>> frustum.n_cells
        6

        """
        frustum_planes = [0] * 24
        self.GetFrustumPlanes(aspect, frustum_planes)
        planes = _vtk.vtkPlanes()
        planes.SetFrustumPlanes(frustum_planes)

        frustum_source = _vtk.vtkFrustumSource()
        frustum_source.ShowLinesOff()
        frustum_source.SetPlanes(planes)
        frustum_source.Update()

        frustum = pyvista.wrap(frustum_source.GetOutput())
        return frustum
def Rectangle(points=None):
    """Create a rectangle defined by 4 points.

    Parameters
    ----------
    points : sequence, optional
        Points of the rectangle.  Defaults to a simple example.

    Returns
    -------
    pyvista.PolyData
        Rectangle mesh.

    Examples
    --------
    >>> import pyvista
    >>> pointa = [1.0, 0.0, 0.0]
    >>> pointb = [1.0, 1.0, 0.0]
    >>> pointc = [0.0, 1.0, 0.0]
    >>> pointd = [0.0, 0.0, 0.0]
    >>> rectangle = pyvista.Rectangle([pointa, pointb, pointc, pointd])
    >>> rectangle.plot(show_edges=True, line_width=5)

    """
    if points is None:
        points = [[1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0],
                  [0.0, 0.0, 0.0]]
    if len(points) != 4:
        raise TypeError('Points must be given as length 4 np.ndarray or list')

    check_valid_vector(points[0], 'points[0]')
    check_valid_vector(points[1], 'points[1]')
    check_valid_vector(points[2], 'points[2]')
    check_valid_vector(points[3], 'points[3]')

    cells = np.array([[4, 0, 1, 2, 3]])
    return pyvista.wrap(pyvista.PolyData(points, cells))
Exemple #12
0
    def showVolume(self):
        data = self.extractVolume()
        self.plotter.clear()
        # self.plotter.show_bounds(grid=True, location='back')

        grid = pv.UniformGrid()
        grid.dimensions = numpy.array(data.shape) + 1
        x1, x2, y1, y2, z1, z2 = self.getVolumeBounds()
        grid.origin = (0, 0, 0)  # The bottom left corner of the data set
        w, h, d = data.shape
        grid.spacing = ((x2 - x1) / w, (y2 - y1) / h, (z2 - z1) / d
                        )  # These are the cell sizes along each axis
        grid.cell_arrays["values"] = data.flatten(order="F")

        # /////////////////////////////////////////////////////////
        # example https://docs.pyvista.org/examples/00-load/create-uniform-grid.html
        if self.type.currentText() == 'vr':
            self.plotter.add_volume(grid,
                                    opacity=self.opacity.currentText(),
                                    cmap=self.cmap.currentText(),
                                    blending=self.blending.currentText())

        elif self.type.currentText() == 'threshold':
            self.plotter.add_mesh_threshold(grid)

        elif self.type.currentText() == 'isovalue':
            self.plotter.add_mesh_isovalue(
                pv.wrap(data))  # TODO, aspect ratio not working

        elif self.type.currentText() == 'slice':
            self.plotter.add_mesh_slice(grid)

        else:
            raise Exception("internal error")

        self.plotter.reset_camera()
Exemple #13
0
def Line(pointa=(-0.5, 0., 0.), pointb=(0.5, 0., 0.), resolution=1):
    """Create a line

    Parameters
    ----------
    pointa : np.ndarray or list
        Location in [x, y, z].

    pointb : np.ndarray or list
        Location in [x, y, z].

    resolution : int
        number of pieces to divide line into
    """
    if np.array(pointa).size != 3:
        raise TypeError('Point A must be a length three tuple of floats.')
    if np.array(pointb).size != 3:
        raise TypeError('Point B must be a length three tuple of floats.')
    src = vtk.vtkLineSource()
    src.SetPoint1(*pointa)
    src.SetPoint2(*pointb)
    src.SetResolution(resolution)
    src.Update()
    return pyvista.wrap(src.GetOutput())
def map_scalars(mapper, scalars):
    """Map scalars to a RGB array.

    Parameters
    ----------
    mapper : vtk.vtkMapper
        Mapper containing lookup table.
    scalars : vtk array, numpy.ndarray, or pyvista.pyvista_ndarray
        Scalars to map

    Returns
    -------
    pyvista.pyvista_ndarray
        Array of mapped scalars.

    """
    if isinstance(scalars, np.ndarray):
        if hasattr(scalars, 'VTKObject') and scalars.VTKObject is not None:
            scalars = scalars.VTKObject
        else:
            scalars = pv._vtk.numpy_to_vtk(scalars)

    table = mapper.GetLookupTable()
    return pv.wrap(table.MapScalars(scalars, 0, 0))[:, :3] / 255
Exemple #15
0
    def to_vtk(mesh, models=None):
        """Convert this mesh object to it's proper VTK or ``pyvista`` data
        object with the given model dictionary as the cell data of that dataset.

        Parameters
        ----------

        models : dict(numpy.ndarray)
            Name('s) and array('s). Match number of cells

        """
        # TODO: mesh.validate()
        converters = {
            "tree": InterfaceVTK.__tree_mesh_to_vtk,
            "tensor": InterfaceVTK.__tensor_mesh_to_vtk,
            "curv": InterfaceVTK.__curvilinear_mesh_to_vtk,
            # TODO: 'CylindricalMesh' : InterfaceVTK.__cyl_mesh_to_vtk,
        }
        key = mesh._meshType.lower()
        try:
            convert = converters[key]
        except KeyError:
            raise RuntimeError(
                "Mesh type `{}` is not currently supported for VTK conversion."
                .format(key))
        # Convert the data object then attempt a wrapping with `pyvista`
        cvtd = convert(mesh, models=models)
        try:
            import pyvista

            cvtd = pyvista.wrap(cvtd)
        except ImportError:
            warnings.warn(
                "For easier use of VTK objects, you should install `pyvista` (the VTK interface): pip install pyvista"
            )
        return cvtd
Exemple #16
0
def sw2vtkImage(swImg, verbose=False):

    # get the numpy array of the shapeworks image for viewing (as if it were in fortran order)
    array = swImg.toArray(for_viewing=True)

    # converting a numpy array to a vtk image using pyvista's wrap function
    vtkImg = pv.wrap(array)

    # set spacing
    spacing = swImg.spacing()
    vtkImg.spacing = [spacing[0], spacing[1], spacing[2]]

    # set origin
    origin = swImg.origin()
    vtkImg.origin = [origin[0], origin[1], origin[2]]

    if verbose:
        print('shapeworks image header information: ')
        print(swImg)

        print('\nvtk image header information: ')
        print(vtkImg)

    return vtkImg
Exemple #17
0
    def add_mesh_slice_spline(self,
                              mesh,
                              generate_triangles=False,
                              n_hanldes=5,
                              resolution=25,
                              widget_color=None,
                              show_ribbon=False,
                              ribbon_color="pink",
                              ribbon_opacity=0.5,
                              **kwargs):
        """Slice a mesh with a spline widget.

        Add a mesh to the scene with a spline widget that is used to slice
        the mesh interactively.

        The sliced mesh is saved to the ``.spline_sliced_meshes`` attribute on
        the plotter.

        Parameters
        ----------
        mesh : pyvista.Common
            The input dataset to add to the scene and slice along the spline

        generate_triangles: bool, optional
            If this is enabled (``False`` by default), the output will be
            triangles otherwise, the output will be the intersection polygons.

        kwargs : dict
            All additional keyword arguments are passed to ``add_mesh`` to
            control how the mesh is displayed.

        """
        name = kwargs.get('name', str(hex(id(mesh))))
        rng = mesh.get_data_range(kwargs.get('scalars', None))
        kwargs.setdefault('clim', kwargs.pop('rng', rng))

        self.add_mesh(mesh.outline(), name=name + "outline", opacity=0.0)

        alg = vtk.vtkCutter()  # Construct the cutter object
        alg.SetInputDataObject(
            mesh)  # Use the grid as the data we desire to cut
        if not generate_triangles:
            alg.GenerateTrianglesOff()

        if not hasattr(self, "spline_sliced_meshes"):
            self.spline_sliced_meshes = []
        spline_sliced_mesh = pyvista.wrap(alg.GetOutput())
        self.spline_sliced_meshes.append(spline_sliced_mesh)

        def callback(spline):
            polyline = spline.GetCell(0)
            # create the plane for clipping
            polyplane = vtk.vtkPolyPlane()
            polyplane.SetPolyLine(polyline)
            alg.SetCutFunction(polyplane)  # the cutter to use the poly planes
            alg.Update()  # Perform the Cut
            spline_sliced_mesh.shallow_copy(alg.GetOutput())

        self.add_spline_widget(callback=callback,
                               bounds=mesh.bounds,
                               factor=1.25,
                               color=widget_color,
                               n_hanldes=n_hanldes,
                               resolution=resolution,
                               show_ribbon=show_ribbon,
                               ribbon_color=ribbon_color,
                               ribbon_opacity=ribbon_opacity)

        actor = self.add_mesh(spline_sliced_mesh, **kwargs)

        return actor
Exemple #18
0
    mesh = pv.read(obj_name)
    shell = mesh.decimate(0.97, volume_preservation=True).extract_surface()
    print(f'Decimation: {len(mesh.points)} -> {len(shell.points)}')

    # warp each point by the normal vectors
    for i in range(1, int(args.distance) + 1):
        print(f'Expanding: {i}')
        shell = shell.compute_normals()
        warp = vtk.vtkWarpVector()
        warp.SetInputData(shell)
        warp.SetInputArrayToProcess(0, 0, 0,
                                    vtk.vtkDataObject.FIELD_ASSOCIATION_POINTS,
                                    vtk.vtkDataSetAttributes.NORMALS)
        warp.SetScaleFactor(2)
        warp.Update()
        shell = pv.wrap(warp.GetOutput())

    expanded_mesh = shell.extract_surface()
    clus = pyacvd.Clustering(expanded_mesh)
    clus.subdivide(3)
    clus.cluster(args.points)
    shell = clus.create_mesh().extract_surface()

    uniform = shell
    p = pv.Plotter(notebook=False, shape=(1, 1))
    p.add_mesh(shell)
    p.add_points(np.asarray(uniform.points),
                 color="r",
                 point_size=8.0,
                 render_points_as_spheres=True)
    p.add_mesh(expanded_mesh, smooth_shading=True)
Exemple #19
0
 def quiver3d(self, x, y, z, u, v, w, color, scale, mode, resolution=8,
              glyph_height=None, glyph_center=None, glyph_resolution=None,
              opacity=1.0, scale_mode='none', scalars=None,
              backface_culling=False, line_width=2., name=None):
     _check_option('mode', mode, ALLOWED_QUIVER_MODES)
     with warnings.catch_warnings():
         warnings.filterwarnings("ignore", category=FutureWarning)
         factor = scale
         vectors = np.c_[u, v, w]
         points = np.vstack(np.c_[x, y, z])
         n_points = len(points)
         cell_type = np.full(n_points, vtk.VTK_VERTEX)
         cells = np.c_[np.full(n_points, 1), range(n_points)]
         args = (cells, cell_type, points)
         if not VTK9:
             args = (np.arange(n_points) * 3,) + args
         grid = UnstructuredGrid(*args)
         grid.point_arrays['vec'] = vectors
         if scale_mode == 'scalar':
             grid.point_arrays['mag'] = np.array(scalars)
             scale = 'mag'
         else:
             scale = False
         if mode == '2darrow':
             return _arrow_glyph(grid, factor), grid
         elif mode == 'arrow':
             alg = _glyph(
                 grid,
                 orient='vec',
                 scalars=scale,
                 factor=factor
             )
             mesh = pyvista.wrap(alg.GetOutput())
         else:
             if mode == 'cone':
                 glyph = vtk.vtkConeSource()
                 glyph.SetCenter(0.5, 0, 0)
                 glyph.SetRadius(0.15)
             elif mode == 'cylinder':
                 glyph = vtk.vtkCylinderSource()
                 glyph.SetRadius(0.15)
             else:
                 assert mode == 'sphere', mode  # guaranteed above
                 glyph = vtk.vtkSphereSource()
             if mode == 'cylinder':
                 if glyph_height is not None:
                     glyph.SetHeight(glyph_height)
                 if glyph_center is not None:
                     glyph.SetCenter(glyph_center)
                 if glyph_resolution is not None:
                     glyph.SetResolution(glyph_resolution)
                 # fix orientation
                 glyph.Update()
                 tr = vtk.vtkTransform()
                 tr.RotateWXYZ(90, 0, 0, 1)
                 trp = vtk.vtkTransformPolyDataFilter()
                 trp.SetInputData(glyph.GetOutput())
                 trp.SetTransform(tr)
                 glyph = trp
             glyph.Update()
             geom = glyph.GetOutput()
             mesh = grid.glyph(orient='vec', scale=scale, factor=factor,
                               geom=geom)
         _add_mesh(
             self.plotter,
             mesh=mesh,
             color=color,
             opacity=opacity,
             backface_culling=backface_culling
         )
Exemple #20
0
             font_family="courier",
             title_font_size=22,
             label_font_size=18,
             n_labels=3,
             fmt="%.0f")

frame_order = np.arange(nr_frames)
frame_order = np.hstack([frame_order, frame_order[::-1]])

opacity = np.ones(255)
opacity[0] = 0

for i, j in enumerate(frame_order):
    # Get data and clip it
    temp = np.copy(data1[..., j])
    pvdata = pv.wrap(temp)

    p = pv.Plotter(window_size=(640, 720))
    p.add_volume(pvdata,
                 stitle="Layers",
                 cmap="Spectral",
                 clim=CLIM,
                 scalar_bar_args=sargs,
                 blending="composite",
                 opacity=opacity,
                 show_scalar_bar=False)
    p.add_text("LN2_MULTILATERATE\nanimated flattening\n[layers]",
               font="courier",
               font_size=16)
    p.set_background("black")
    p.camera_position = CAMPOS
for path in prm['output_folder_meshes'].glob(
        '*.off'):  #this script works on the output folder of the CGAL meshing

    if verbose:
        print(' post processing mesh : {}'.format(path.name),
              flush=True,
              end="")
    f_out = prm['output_folder_meshes'] / (path.stem + ".stl")

    #convert off to stl
    d_path_mesh[path] = trimesh.load_mesh(str(path), process=False)
    if verbose:
        print(' -> converted to stl'.format(path.name), flush=True, end="")

    ## remesh
    mesh = pyvista.wrap(d_path_mesh[path])  #needs pyvista 0.27.x
    clus = clustering.Clustering(mesh)  #wrap gives VTK polydata
    clus.subdivide(3)
    clus.cluster(1200)
    remesh = clus.create_mesh()
    write_stl_file(remesh, f_out)
    if verbose: print(' -> remeshed'.format(path.name), flush=True, end="")

    ## repair
    try:
        _meshfix.clean_from_file(str(f_out),
                                 str(f_out))  #repairing, just in case
        if verbose: print(' -> repaired'.format(path.name), flush=True, end="")

        #in very rare ocassions the mesh normals are pointing inwards (spot by negative volume)
        tmh_mesh = trimesh.load_mesh(str(f_out), process=False)
Exemple #22
0
- `trimesh.Trimesh` meshes
- VTK objects

This allows for the "best of both worlds" programming special to
Python due to its modularity.  If there's some limitation of pyvista
(or trimesh), then you can adapt your scripts to use the best features
of more than one module.

"""
import pyvista as pv

###############################################################################
# Wrap a point cloud composed of random points from numpy
import numpy as np
points = np.random.random((30, 3))
cloud = pv.wrap(points)
pv.plot(cloud,
        scalars=points[:, 2],
        render_points_as_spheres=True,
        point_size=50,
        opacity=points[:, 0],
        cpos='xz')

###############################################################################
# Wrap an instance of Trimesh
import trimesh
points = [[0, 0, 0], [0, 0, 1], [0, 1, 0]]
faces = [[0, 1, 2]]
tmesh = trimesh.Trimesh(points, faces=faces, process=False)
mesh = pv.wrap(tmesh)
print(mesh)
Exemple #23
0
    def add_mesh(self,
                 mesh,
                 color=None,
                 scalars=None,
                 opacity=1.0,
                 smooth_shading=False):
        """Add a PyVista/VTK mesh or dataset.

        Adds any PyVista/VTK mesh that itkwidgets can wrap to the
        scene.

        Parameters
        ----------
        mesh : pyvista.DataSet or pyvista.MultiBlock
            Any PyVista or VTK mesh is supported. Also, any dataset
            that :func:`pyvista.wrap` can handle including NumPy arrays of XYZ
            points.

        color : string or 3 item list, optional, defaults to white
            Use to make the entire mesh have a single solid color.
            Either a string, RGB list, or hex color string.  For example:
            ``color='white'``, ``color='w'``, ``color=[1, 1, 1]``, or
            ``color='#FFFFFF'``. Color will be overridden if scalars are
            specified.

        scalars : str or numpy.ndarray, optional
            Scalars used to "color" the mesh.  Accepts a string name of an
            array that is present on the mesh or an array equal
            to the number of cells or the number of points in the
            mesh.  Array should be sized as a single vector. If both
            ``color`` and ``scalars`` are ``None``, then the active scalars are
            used.

        opacity : float, optional
            Opacity of the mesh. If a single float value is given, it will be
            the global opacity of the mesh and uniformly applied everywhere -
            should be between 0 and 1.  Default 1.0

        smooth_shading : bool, optional
            Smooth mesh surface mesh by taking into account surface
            normals.  Surface will appear smoother while sharp edges
            will still look sharp.  Default False.

        """
        if not pv.is_pyvista_dataset(mesh):
            mesh = pv.wrap(mesh)

        # smooth shading requires point normals to be freshly computed
        if smooth_shading:
            # extract surface if mesh is exterior
            if not isinstance(mesh, pv.PolyData):
                grid = mesh
                mesh = grid.extract_surface()
                ind = mesh.point_arrays['vtkOriginalPointIds']
                # remap scalars
                if isinstance(scalars, np.ndarray):
                    scalars = scalars[ind]

            mesh.compute_normals(cell_normals=False, inplace=True)
        elif 'Normals' in mesh.point_arrays:
            # if 'normals' in mesh.point_arrays:
            mesh.point_arrays.pop('Normals')

        # make the scalars active
        if isinstance(scalars, str):
            if scalars in mesh.point_arrays or scalars in mesh.cell_arrays:
                array = mesh[scalars].copy()
            else:
                raise ValueError(f'Scalars ({scalars}) not in mesh')
            mesh[scalars] = array
            mesh.active_scalars_name = scalars
        elif isinstance(scalars, np.ndarray):
            array = scalars
            scalar_name = '_scalars'
            mesh[scalar_name] = array
            mesh.active_scalars_name = scalar_name
        elif color is not None:
            mesh.active_scalars_name = None

        # itkwidgets does not support VTK_ID_TYPE
        if 'vtkOriginalPointIds' in mesh.point_arrays:
            mesh.point_arrays.pop('vtkOriginalPointIds')

        if 'vtkOriginalCellIds' in mesh.cell_arrays:
            mesh.cell_arrays.pop('vtkOriginalCellIds')

        from itkwidgets._transform_types import to_geometry
        mesh = to_geometry(mesh)
        self._geometries.append(mesh)
        self._geometry_colors.append(pv.parse_color(color))
        self._geometry_opacities.append(opacity)
Exemple #24
0
def plot(d):
    data = pv.wrap(d)
    data.plot(volume=True)
Exemple #25
0
def CircularArc(pointa,
                pointb,
                center,
                resolution=100,
                normal=None,
                polar=None,
                angle=None,
                negative=False):
    """Create a circular arc defined by two endpoints and a center.

    The number of segments composing the polyline is controlled by
    setting the object resolution.  Alternatively, one can use a
    better API (that does not allow for inconsistent nor ambiguous
    inputs), using a starting point (polar vector, measured from the
    arc's center), a normal to the plane of the arc, and an angle
    defining the arc length.

    Parameters
    ----------
    pointa : np.ndarray or list
        Position of the first end point.

    pointb : np.ndarray or list
        Position of the other end point.

    center : np.ndarray or list
        Center of the circle that defines the arc.

    resolution : int, optional
        The number of segments of the polyline that draws the arc.
        Resolution of 1 will just create a line.

    normal : np.ndarray or list
        The normal vector to the plane of the arc.  By default it
        points in the positive Z direction.

    polar : np.ndarray or list
        (starting point of the arc).  By default it is the unit vector
        in the positive x direction. Note: This is only used when
        normal has been input.

    angle : float
        Arc length (in degrees), beginning at the polar vector.  The
        direction is counterclockwise by default; a negative value
        draws the arc in the clockwise direction.  Note: This is only
        used when normal has been input.

    negative : bool, optional
        By default the arc spans the shortest angular sector point1 and point2.

        By setting this to true, the longest angular sector is used
        instead (i.e. the negative coterminal angle to the shortest
        one). This is only used when normal has not been input

    Examples
    --------
    Quarter arc centered at the origin in the xy plane

    >>> import pyvista
    >>> arc = pyvista.CircularArc([-1, 0, 0], [0, 1, 0], [0, 0, 0])
    >>> pl = pyvista.Plotter()
    >>> _ = pl.add_mesh(arc, color='k', line_width=4)
    >>> _ = pl.show_bounds(location='all')
    >>> _ = pl.view_xy()
    >>> pl.show() # doctest:+SKIP

    Quarter arc centered at the origin in the xz plane

    >>> arc = pyvista.CircularArc([-1, 0, 0], [1, 0, 0], [0, 0, 0], normal=[0, 0, 1])
    >>> arc.plot() # doctest:+SKIP
    """
    check_valid_vector(pointa, 'pointa')
    check_valid_vector(pointb, 'pointb')
    check_valid_vector(center, 'center')

    # fix half-arc bug: if a half arc travels directly through the
    # center point, it becomes a line
    pointb = list(pointb)
    pointb[0] -= 1E-10
    pointb[1] -= 1E-10

    arc = vtk.vtkArcSource()
    arc.SetPoint1(*pointa)
    arc.SetPoint2(*pointb)
    arc.SetCenter(*center)
    arc.SetResolution(resolution)
    arc.SetNegative(negative)

    if normal is not None:
        arc.UseNormalAndAngleOn()
        check_valid_vector(normal, 'normal')
        arc.SetNormal(*normal)

        if polar is not None:
            check_valid_vector(polar, 'polar')
            arc.SetPolarVector(*polar)

        if angle is not None:
            arc.SetAngle(angle)

    arc.Update()
    return pyvista.wrap(arc.GetOutput())
Exemple #26
0
    def add_mesh_isovalue(self,
                          mesh,
                          scalars=None,
                          compute_normals=False,
                          compute_gradients=False,
                          compute_scalars=True,
                          preference='point',
                          title=None,
                          pointa=(.4, .9),
                          pointb=(.9, .9),
                          widget_color=None,
                          **kwargs):
        """Create a contour of a mesh with a slider.

        Add a mesh to the scene with a slider widget that is used to
        contour at an isovalue of the *point* data on the mesh interactively.

        The isovalue mesh is saved to the ``.isovalue_meshes`` attribute on
        the plotter.

        Parameters
        ----------
        mesh : pyvista.Common
            The input dataset to add to the scene and contour

        scalars : str
            The string name of the scalars on the mesh to threshold and display

        kwargs : dict
            All additional keyword arguments are passed to ``add_mesh`` to
            control how the mesh is displayed.

        """
        if isinstance(mesh, pyvista.MultiBlock):
            raise TypeError(
                'MultiBlock datasets are not supported for this widget.')
        name = kwargs.get('name', str(hex(id(mesh))))
        # set the array to contour on
        if mesh.n_arrays < 1:
            raise AssertionError(
                'Input dataset for the contour filter must have data arrays.')
        if scalars is None:
            field, scalars = mesh.active_scalars_info
        else:
            _, field = get_array(mesh,
                                 scalars,
                                 preference=preference,
                                 info=True)
        # NOTE: only point data is allowed? well cells works but seems buggy?
        if field != pyvista.POINT_DATA_FIELD:
            raise AssertionError(
                'Contour filter only works on Point data. Array ({}) is in the Cell data.'
                .format(scalars))

        rng = mesh.get_data_range(scalars)
        kwargs.setdefault('clim', kwargs.pop('rng', rng))
        if title is None:
            title = scalars

        alg = vtk.vtkContourFilter()
        alg.SetInputDataObject(mesh)
        alg.SetComputeNormals(compute_normals)
        alg.SetComputeGradients(compute_gradients)
        alg.SetComputeScalars(compute_scalars)
        alg.SetInputArrayToProcess(0, 0, 0, field, scalars)
        alg.SetNumberOfContours(1)  # Only one contour level

        self.add_mesh(mesh.outline(), name=name + "outline", opacity=0.0)

        if not hasattr(self, "isovalue_meshes"):
            self.isovalue_meshes = []
        isovalue_mesh = pyvista.wrap(alg.GetOutput())
        self.isovalue_meshes.append(isovalue_mesh)

        def callback(value):
            alg.SetValue(0, value)
            alg.Update()
            isovalue_mesh.shallow_copy(alg.GetOutput())

        self.add_slider_widget(callback=callback,
                               rng=rng,
                               title=title,
                               color=widget_color,
                               pointa=pointa,
                               pointb=pointb)

        kwargs.setdefault("reset_camera", False)
        actor = self.add_mesh(isovalue_mesh, scalars=scalars, **kwargs)

        return actor
Exemple #27
0
    def add_mesh_threshold(self,
                           mesh,
                           scalars=None,
                           invert=False,
                           widget_color=None,
                           preference='cell',
                           title=None,
                           pointa=(.4, .9),
                           pointb=(.9, .9),
                           continuous=False,
                           **kwargs):
        """Apply a threshold on a mesh with a slider.

        Add a mesh to the scene with a slider widget that is used to
        threshold the mesh interactively.

        The threshold mesh is saved to the ``.threshold_meshes`` attribute on
        the plotter.

        Parameters
        ----------
        mesh : pyvista.Common
            The input dataset to add to the scene and threshold

        scalars : str
            The string name of the scalars on the mesh to threshold and display

        invert : bool
            Invert/flip the threshold

        kwargs : dict
            All additional keyword arguments are passed to ``add_mesh`` to
            control how the mesh is displayed.

        """
        if isinstance(mesh, pyvista.MultiBlock):
            raise TypeError(
                'MultiBlock datasets are not supported for threshold widget.')
        name = kwargs.get('name', str(hex(id(mesh))))
        if scalars is None:
            field, scalars = mesh.active_scalars_info
        arr, field = get_array(mesh, scalars, preference=preference, info=True)
        if arr is None:
            raise AssertionError('No arrays present to threshold.')
        rng = mesh.get_data_range(scalars)
        kwargs.setdefault('clim', kwargs.pop('rng', rng))
        if title is None:
            title = scalars

        self.add_mesh(mesh.outline(), name=name + "outline", opacity=0.0)

        alg = vtk.vtkThreshold()
        alg.SetInputDataObject(mesh)
        alg.SetInputArrayToProcess(
            0, 0, 0, field,
            scalars)  # args: (idx, port, connection, field, name)
        alg.SetUseContinuousCellRange(continuous)

        if not hasattr(self, "threshold_meshes"):
            self.threshold_meshes = []
        threshold_mesh = pyvista.wrap(alg.GetOutput())
        self.threshold_meshes.append(threshold_mesh)

        def callback(value):
            if invert:
                alg.ThresholdByLower(value)
            else:
                alg.ThresholdByUpper(value)
            alg.Update()
            threshold_mesh.shallow_copy(alg.GetOutput())

        self.add_slider_widget(callback=callback,
                               rng=rng,
                               title=title,
                               color=widget_color,
                               pointa=pointa,
                               pointb=pointb)

        kwargs.setdefault("reset_camera", False)
        actor = self.add_mesh(threshold_mesh, scalars=scalars, **kwargs)

        return actor
Exemple #28
0
    def add_mesh_slice(self,
                       mesh,
                       normal='x',
                       generate_triangles=False,
                       widget_color=None,
                       assign_to_axis=None,
                       tubing=False,
                       origin_translation=True,
                       outline_translation=False,
                       implicit=True,
                       **kwargs):
        """Slice a mesh using a plane widget.

        Add a mesh to the scene with a plane widget that is used to slice
        the mesh interactively.

        The sliced mesh is saved to the ``.plane_sliced_meshes`` attribute on
        the plotter.

        Parameters
        ----------
        mesh : pyvista.Common
            The input dataset to add to the scene and slice

        normal : str or tuple(float)
            The starting normal vector of the plane

        generate_triangles: bool, optional
            If this is enabled (``False`` by default), the output will be
            triangles otherwise, the output will be the intersection polygons.

        kwargs : dict
            All additional keyword arguments are passed to ``add_mesh`` to
            control how the mesh is displayed.

        """
        name = kwargs.get('name', str(hex(id(mesh))))
        rng = mesh.get_data_range(kwargs.get('scalars', None))
        kwargs.setdefault('clim', kwargs.pop('rng', rng))

        self.add_mesh(mesh.outline(), name=name + "outline", opacity=0.0)

        alg = vtk.vtkCutter()  # Construct the cutter object
        alg.SetInputDataObject(
            mesh)  # Use the grid as the data we desire to cut
        if not generate_triangles:
            alg.GenerateTrianglesOff()

        if not hasattr(self, "plane_sliced_meshes"):
            self.plane_sliced_meshes = []
        plane_sliced_mesh = pyvista.wrap(alg.GetOutput())
        self.plane_sliced_meshes.append(plane_sliced_mesh)

        def callback(normal, origin):
            # create the plane for clipping
            plane = generate_plane(normal, origin)
            alg.SetCutFunction(plane)  # the cutter to use the plane we made
            alg.Update()  # Perform the Cut
            plane_sliced_mesh.shallow_copy(alg.GetOutput())

        self.add_plane_widget(callback=callback,
                              bounds=mesh.bounds,
                              factor=1.25,
                              normal=normal,
                              color=widget_color,
                              tubing=tubing,
                              assign_to_axis=assign_to_axis,
                              origin_translation=origin_translation,
                              outline_translation=outline_translation,
                              implicit=implicit,
                              origin=mesh.center)

        actor = self.add_mesh(plane_sliced_mesh, **kwargs)

        return actor
Exemple #29
0
    def add_mesh_clip_plane(self,
                            mesh,
                            normal='x',
                            invert=False,
                            widget_color=None,
                            value=0.0,
                            assign_to_axis=None,
                            tubing=False,
                            origin_translation=True,
                            outline_translation=False,
                            implicit=True,
                            **kwargs):
        """Clip a mesh using a plane widget.

        Add a mesh to the scene with a plane widget that is used to clip
        the mesh interactively.

        The clipped mesh is saved to the ``.plane_clipped_meshes`` attribute on
        the plotter.

        Parameters
        ----------
        mesh : pyvista.Common
            The input dataset to add to the scene and clip

        normal : str or tuple(float)
            The starting normal vector of the plane

        invert : bool
            Flag on whether to flip/invert the clip

        kwargs : dict
            All additional keyword arguments are passed to ``add_mesh`` to
            control how the mesh is displayed.

        """
        name = kwargs.get('name', str(hex(id(mesh))))
        rng = mesh.get_data_range(kwargs.get('scalars', None))
        kwargs.setdefault('clim', kwargs.pop('rng', rng))

        self.add_mesh(mesh.outline(), name=name + "outline", opacity=0.0)

        if isinstance(mesh, vtk.vtkPolyData):
            alg = vtk.vtkClipPolyData()
        # elif isinstance(mesh, vtk.vtkImageData):
        #     alg = vtk.vtkClipVolume()
        #     alg.SetMixed3DCellGeneration(True)
        else:
            alg = vtk.vtkTableBasedClipDataSet()
        alg.SetInputDataObject(
            mesh)  # Use the grid as the data we desire to cut
        alg.SetValue(value)
        alg.SetInsideOut(invert)  # invert the clip if needed

        if not hasattr(self, "plane_clipped_meshes"):
            self.plane_clipped_meshes = []
        plane_clipped_mesh = pyvista.wrap(alg.GetOutput())
        self.plane_clipped_meshes.append(plane_clipped_mesh)

        def callback(normal, origin):
            function = generate_plane(normal, origin)
            alg.SetClipFunction(function)  # the implicit function
            alg.Update()  # Perform the Cut
            plane_clipped_mesh.shallow_copy(alg.GetOutput())

        self.add_plane_widget(callback=callback,
                              bounds=mesh.bounds,
                              factor=1.25,
                              normal=normal,
                              color=widget_color,
                              tubing=tubing,
                              assign_to_axis=assign_to_axis,
                              origin_translation=origin_translation,
                              outline_translation=outline_translation,
                              implicit=implicit,
                              origin=mesh.center)

        actor = self.add_mesh(plane_clipped_mesh, **kwargs)

        return actor
Exemple #30
0
    def add_mesh_clip_box(self,
                          mesh,
                          invert=False,
                          rotation_enabled=True,
                          widget_color=None,
                          outline_translation=True,
                          **kwargs):
        """Clip a mesh using a box widget.

        Add a mesh to the scene with a box widget that is used to clip
        the mesh interactively.

        The clipped mesh is saved to the ``.box_clipped_meshes`` attribute on
        the plotter.

        Parameters
        ----------
        mesh : pyvista.Common
            The input dataset to add to the scene and clip

        invert : bool
            Flag on whether to flip/invert the clip

        rotation_enabled : bool
            If ``False``, the box widget cannot be rotated and is strictly
            orthogonal to the cartesian axes.

        kwargs : dict
            All additional keyword arguments are passed to ``add_mesh`` to
            control how the mesh is displayed.

        """
        name = kwargs.get('name', str(hex(id(mesh))))
        rng = mesh.get_data_range(kwargs.get('scalars', None))
        kwargs.setdefault('clim', kwargs.pop('rng', rng))

        self.add_mesh(mesh.outline(), name=name + "outline", opacity=0.0)

        port = 1 if invert else 0

        alg = vtk.vtkBoxClipDataSet()
        alg.SetInputDataObject(mesh)
        alg.GenerateClippedOutputOn()

        if not hasattr(self, "box_clipped_meshes"):
            self.box_clipped_meshes = []
        box_clipped_mesh = pyvista.wrap(alg.GetOutput(port))
        self.box_clipped_meshes.append(box_clipped_mesh)

        def callback(planes):
            bounds = []
            for i in range(planes.GetNumberOfPlanes()):
                plane = planes.GetPlane(i)
                bounds.append(plane.GetNormal())
                bounds.append(plane.GetOrigin())

            alg.SetBoxClip(*bounds)
            alg.Update()
            box_clipped_mesh.shallow_copy(alg.GetOutput(port))

        self.add_box_widget(callback=callback,
                            bounds=mesh.bounds,
                            factor=1.25,
                            rotation_enabled=rotation_enabled,
                            use_planes=True,
                            color=widget_color,
                            outline_translation=outline_translation)

        actor = self.add_mesh(box_clipped_mesh, reset_camera=False, **kwargs)

        return actor