Esempio n. 1
0
def pyvista_polydata_to_polymesh(obj):
    """Import a mesh from ``pyvista`` or ``vtk``.

    Copies over the active scalars and only the active scalars.

    Parameters
    ----------
    obj : pyvista compatible object
        Any object compatible with pyvista.  Includes most ``vtk``
        objects.

    Returns
    -------
    PolyMesh
        ``ipygany.PolyMesh`` object.
    """
    # attempt to wrap non-pyvista objects
    if not pv.is_pyvista_dataset(obj):  # pragma: no cover
        mesh = pv.wrap(obj)
        if not pv.is_pyvista_dataset(mesh):
            raise TypeError(f'Object type ({type(mesh)}) cannot be converted to '
                            'a pyvista dataset')
    else:
        mesh = obj

    # PolyMesh requires vertices and triangles, so we need to
    # convert the mesh to an all triangle polydata
    if not isinstance(obj, pv.PolyData):
        # unlikely case that mesh does not have extract_surface
        if not hasattr(mesh, 'extract_surface'):  # pragma: no cover
            mesh = mesh.cast_to_unstructured_grid()
        surf = mesh.extract_surface()
    else:
        surf = mesh

    # convert to an all-triangular surface
    if surf.is_all_triangles():
        trimesh = surf
    else:
        trimesh = surf.triangulate()

    # finally, pass the triangle vertices to PolyMesh
    triangle_indices = trimesh.faces.reshape(-1, 4)[:, 1:]

    if not triangle_indices.size:
        warnings.warn('Unable to convert mesh to triangular PolyMesh')

    # only copy active scalars
    data = []
    if trimesh.active_scalars is not None:
        arr = array('f', trimesh.active_scalars)
        components = [ipygany.Component('X1', arr)]
        data = [ipygany.Data(trimesh.active_scalars_name, components)]

    # for speed, only convert the active scalars later
    return PolyMesh(
        vertices=trimesh.points,
        triangle_indices=triangle_indices,
        data=data
    )
Esempio n. 2
0
def extract_surface_mesh(obj):
    """Extract a surface mesh from a pyvista or vtk dataset.

    Parameters
    ----------
    obj : pyvista compatible object
        Any object compatible with pyvista.  Includes most ``vtk``
        objects.

    Returns
    -------
    pyvista.PolyData
        Surface mesh

    """
    # attempt to wrap non-pyvista objects
    if not pv.is_pyvista_dataset(obj):  # pragma: no cover
        mesh = pv.wrap(obj)
        if not pv.is_pyvista_dataset(mesh):
            raise TypeError(
                f'Object type ({type(mesh)}) cannot be converted to '
                'a pyvista dataset')
    else:
        mesh = obj

    if not isinstance(obj, pv.PolyData):
        # unlikely case that mesh does not have extract_surface
        if not hasattr(mesh, 'extract_surface'):  # pragma: no cover
            mesh = mesh.cast_to_unstructured_grid()
        return mesh.extract_surface()

    return mesh
Esempio n. 3
0
def test_multi_block_copy(ant, sphere, uniform, airplane, globe):
    multi = multi_from_datasets(ant, sphere, uniform, airplane, globe)
    # Now check everything
    multi_copy = multi.copy()
    assert multi.n_blocks == 5 == multi_copy.n_blocks
    assert id(multi[0]) != id(multi_copy[0])
    assert id(multi[-1]) != id(multi_copy[-1])
    for i in range(multi_copy.n_blocks):
        assert pyvista.is_pyvista_dataset(multi_copy.GetBlock(i))
    # Now check shallow
    multi_copy = multi.copy(deep=False)
    assert multi.n_blocks == 5 == multi_copy.n_blocks
    assert id(multi[0]) == id(multi_copy[0])
    assert id(multi[-1]) == id(multi_copy[-1])
    for i in range(multi_copy.n_blocks):
        assert pyvista.is_pyvista_dataset(multi_copy.GetBlock(i))
Esempio n. 4
0
    def add_mesh(self, mesh, color=None, scalars=None, clim=None,
                 opacity=1.0, n_colors=256, cmap='Viridis (matplotlib)',
                 **kwargs):
        """Add mesh to the scene."""
        if not pv.is_pyvista_dataset(mesh):
            mesh = pv.wrap(mesh)
        mesh = mesh.copy()
        if scalars is None and color is None:
            scalars = mesh.active_scalars_name

        if scalars is not None:
            array = mesh[scalars].copy()
            mesh.clear_arrays()
            mesh[scalars] = array
            mesh.active_scalars_name = scalars
        elif color is not None:
            mesh.clear_arrays()


        mesh = to_geometry(mesh)
        self._geometries.append(mesh)
        self._geometry_colors.append(pv.parse_color(color))
        self._geometry_opacities.append(opacity)
        self._cmap = cmap

        return
Esempio n. 5
0
def add_textures(output, textures, elname):
    """Add textures to a pyvista data object"""
    if not is_pyvista_dataset(output):
        output = pyvista.wrap(output)

    for i, tex in enumerate(textures):
        # Now map the coordinates for the texture
        tmp = output.texture_map_to_plane(origin=tex.origin,
                                          point_u=tex.origin + tex.axis_u,
                                          point_v=tex.origin + tex.axis_v)
        # Grab the texture coordinates
        tcoord = tmp.GetPointData().GetTCoords()
        name = tex.name
        if name is None or name == '':
            name = '{}-texture-{}'.format(elname, i)
        tcoord.SetName(name)
        # Add these coordinates to the PointData of the output
        # NOTE: Let pyvista handle setting the TCoords because of how VTK cleans
        #       up old TCoords
        output.GetPointData().AddArray(tcoord)
        # Add the vtkTexture to the output
        img = np.array(Image.open(tex.image))
        tex.image.seek(0)  # Reset the image bytes in case it is accessed again
        if img.shape[2] > 3:
            img = img[:, :, 0:3]
        vtexture = pyvista.numpy_to_texture(img)
        output.textures[name] = vtexture
        output._activate_texture(name)

    return output
Esempio n. 6
0
    def add_points(self, points, color=None):
        """Add XYZ points to the scene."""
        if pv.is_pyvista_dataset(points):
            point_array = points.points
        else:
            point_array = points

        self._point_set_colors.append(pv.parse_color(color))
        self._point_sets.append(point_array)
Esempio n. 7
0
def test_multi_block_copy():
    multi = pyvista.MultiBlock()
    # Add examples
    multi.append(ex.load_ant())
    multi.append(ex.load_sphere())
    multi.append(ex.load_uniform())
    multi.append(ex.load_airplane())
    multi.append(ex.load_globe())
    # Now check everything
    newobj = multi.copy()
    assert multi.n_blocks == 5 == newobj.n_blocks
    assert id(multi[0]) != id(newobj[0])
    assert id(multi[-1]) != id(newobj[-1])
    for i in range(newobj.n_blocks):
        assert pyvista.is_pyvista_dataset(newobj.GetBlock(i))
    # Now check shallow
    newobj = multi.copy(deep=False)
    assert multi.n_blocks == 5 == newobj.n_blocks
    assert id(multi[0]) == id(newobj[0])
    assert id(multi[-1]) == id(newobj[-1])
    for i in range(newobj.n_blocks):
        assert pyvista.is_pyvista_dataset(newobj.GetBlock(i))
    return
Esempio n. 8
0
    def add_points(self, points, color=None, point_size=3.0):
        """Add points to plotter.

        Parameters
        ----------
        points : np.ndarray or pyvista.DataSet
            n x 3 numpy array of points or pyvista dataset with points.

        color : string or 3 item list, optional. Color of points (if visible).
            Either a string, rgb list, or hex color string.  For example:

            ``color='white'``
            ``color='w'``
            ``color=[1, 1, 1]``
            ``color='#FFFFFF'``

        point_size : float, optional
            Point size of any nodes in the dataset plotted. Also applicable
            when style='points'. Default ``3.0``

        Examples
        --------
        Add 10 random points to the plotter

        >>> add_points(np.random.random((10, 3)), 'r', 10)  # doctest:+SKIP
        """
        if pv.is_pyvista_dataset(points):
            point_array = points.points
        else:
            point_array = points


        # style : str, optional
        #     How to represent the point set. One of ``'hidden'``,
        #     ``'points'``, or ``'spheres'``.


        # if style not in ['hidden', 'points', 'spheres']:
        #     raise ValueError("``style`` must be either 'hidden', 'points', or"
        #                      "'spheres'")

        if not isinstance(point_size, (int, float)):
            raise TypeError('``point_size`` parameter must be a float')

        self._point_set_sizes.append(point_size)
        self._point_set_colors.append(pv.parse_color(color))
        self._point_sets.append(point_array)
Esempio n. 9
0
def segment_poly_cells(mesh):
    """Segment lines from a mesh into line segments."""
    if not pv.is_pyvista_dataset(mesh):  # pragma: no cover
        mesh = pv.wrap(mesh)
    polylines = []
    i, offset = 0, 0
    cc = mesh.lines  # fetch up front
    while i < mesh.n_cells:
        nn = cc[offset]
        polylines.append(cc[offset + 1:offset + 1 + nn])
        offset += nn + 1
        i += 1

    lines = []
    for poly in polylines:
        lines.append(np.column_stack((poly[:-1], poly[1:])))
    return np.vstack(lines)
Esempio n. 10
0
    def add_points(self, points, color=None):
        """Add points to plotting object.

        Parameters
        ----------
        points : np.ndarray or pyvista.Common
            n x 3 numpy array of points or pyvista dataset with points.

        color : string or 3 item list, optional. Color of points (if visible).
            Either a string, rgb list, or hex color string.  For example:

            color='white'
            color='w'
            color=[1, 1, 1]
            color='#FFFFFF'
        """
        if pv.is_pyvista_dataset(points):
            point_array = points.points
        else:
            point_array = points

        self._point_set_colors.append(pv.parse_color(color))
        self._point_sets.append(point_array)
Esempio n. 11
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)
Esempio n. 12
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)
Esempio n. 13
0
def generate_on_mesh(f_cls,
                     mesh,
                     points="centroids",
                     direction="all",
                     name="field",
                     **kwargs):
    """Generate a field on a given meshio, ogs5py or pyvista mesh.

    Parameters
    ----------
    f_cls : :any:`Field`
        The field class in use.
    mesh : meshio.Mesh or ogs5py.MSH or PyVista mesh
        The given meshio, ogs5py, or PyVista mesh
    points : :class:`str`, optional
        The points to evaluate the field at.
        Either the "centroids" of the mesh cells
        (calculated as mean of the cell vertices) or the "points"
        of the given mesh.
        Default: "centroids"
    direction : :class:`str` or :class:`list`, optional
        Here you can state which direction should be choosen for
        lower dimension. For example, if you got a 2D mesh in xz direction,
        you have to pass "xz". By default, all directions are used.
        One can also pass a list of indices.
        Default: "all"
    name : :class:`str` or :class:`list` of :class:`str`, optional
        Name(s) to store the field(s) in the given mesh as point_data or
        cell_data. If to few names are given, digits will be appended.
        Default: "field"
    **kwargs
        Keyword arguments forwareded to `Field.__call__`.

    Notes
    -----
    This will store the field in the given mesh under the given name,
    if a meshio or PyVista mesh was given.

    See: https://github.com/nschloe/meshio

    See: https://github.com/GeoStat-Framework/ogs5py

    See: https://github.com/pyvista/pyvista
    """
    has_pyvista = False
    has_ogs5py = False

    try:
        import pyvista as pv

        has_pyvista = True
    except ImportError:
        pass
    try:
        import ogs5py as ogs

        has_ogs5py = True
    except ImportError:
        pass

    if isinstance(direction, str) and direction == "all":
        select = list(range(f_cls.dim))
    elif isinstance(direction, str):
        select = _get_select(direction)[:f_cls.dim]
    else:
        select = direction[:f_cls.dim]
    if len(select) < f_cls.dim:
        raise ValueError(
            f"Field.mesh: need at least {f_cls.dim} direction(s), "
            f"got '{direction}'")
    # convert pyvista mesh
    if has_pyvista and pv.is_pyvista_dataset(mesh):
        if points == "centroids":
            pnts = mesh.cell_centers().points.T[select]
        else:
            pnts = mesh.points.T[select]
        out = f_cls.unstructured(pos=pnts, **kwargs)
        # Deal with the output
        fields = [out] if isinstance(out, np.ndarray) else out
        if f_cls.value_type == "vector":
            fields = [f.T for f in fields]
        for f_name, field in zip(_names(name, len(fields)), fields):
            mesh[f_name] = field
    # convert ogs5py mesh
    elif has_ogs5py and isinstance(mesh, ogs.MSH):
        if points == "centroids":
            pnts = mesh.centroids_flat.T[select]
        else:
            pnts = mesh.NODES.T[select]
        out = f_cls.unstructured(pos=pnts, **kwargs)
    # convert meshio mesh
    elif isinstance(mesh, meshio.Mesh):
        if points == "centroids":
            # define unique order of cells
            offset = []
            length = []
            mesh_dim = mesh.points.shape[1]
            if mesh_dim < f_cls.dim:
                raise ValueError("Field.mesh: mesh dimension too low!")
            pnts = np.empty((0, mesh_dim), dtype=np.double)
            for cell in mesh.cells:
                cell_points = cell[1] if MESHIO_VERSION < [5, 1] else cell.data
                pnt = np.mean(mesh.points[cell_points], axis=1)
                offset.append(pnts.shape[0])
                length.append(pnt.shape[0])
                pnts = np.vstack((pnts, pnt))
            # generate pos for __call__
            pnts = pnts.T[select]
            out = f_cls.unstructured(pos=pnts, **kwargs)
            fields = [out] if isinstance(out, np.ndarray) else out
            if f_cls.value_type == "vector":
                fields = [f.T for f in fields]
            f_lists = []
            for field in fields:
                f_list = []
                for off, leng in zip(offset, length):
                    f_list.append(field[off:off + leng])
                f_lists.append(f_list)
            for f_name, f_list in zip(_names(name, len(f_lists)), f_lists):
                mesh.cell_data[f_name] = f_list
        else:
            out = f_cls.unstructured(pos=mesh.points.T[select], **kwargs)
            fields = [out] if isinstance(out, np.ndarray) else out
            if f_cls.value_type == "vector":
                fields = [f.T for f in fields]
            for f_name, field in zip(_names(name, len(fields)), fields):
                mesh.point_data[f_name] = field
    else:
        raise ValueError("Field.mesh: Unknown mesh format!")
    return out
Esempio n. 14
0
def voxelize(mesh, density=None, check_surface=True):
    """Voxelize mesh to UnstructuredGrid.

    Parameters
    ----------
    density : float or list
        The uniform size of the voxels when single float passed.
        A list of densities along x,y,z directions.
        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.

    Returns
    -------
    vox : pyvista.core.pointset.UnstructuredGrid
        voxelized unstructured grid for original mesh

    Examples
    --------
    This example creates an equal density voxelized mesh.

    >>> import pyvista as pv
    >>> import pyvista.examples as ex
    >>> mesh = pv.PolyData(ex.load_uniform().points)
    >>> vox = pv.voxelize(mesh, density=0.5)

    This example creates a voxelized mesh using unequal density dimensions

    >>> import pyvista as pv
    >>> import pyvista.examples as ex
    >>> mesh = pv.PolyData(ex.load_uniform().points)
    >>> vox = pv.voxelize(mesh, density=[0.5, 0.9, 1.4])

    """
    if not pyvista.is_pyvista_dataset(mesh):
        mesh = pyvista.wrap(mesh)
    if density is None:
        density = mesh.length / 100
    if isinstance(density, (int, float)):
        density_x, density_y, density_z = [density] * 3
    if isinstance(density, (list, set, tuple)):
        density_x, density_y, density_z = density

    x_min, x_max, y_min, y_max, z_min, z_max = mesh.bounds
    x = np.arange(x_min, x_max, density_x)
    y = np.arange(y_min, y_max, density_y)
    z = np.arange(z_min, z_max, density_z)
    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
    vox = ugrid.extract_points(mask)
    return vox