コード例 #1
0
def load_VTK(filename, name=None):
    """Loads VTK file format in the legacy format (vtk file extension).

    It relies on the reader from the VTK library.

    Parameters
    ----------
    filename: str
        name of the meh file on disk

    Returns
    -------
    Mesh
        the loaded mesh

    Note
    ----
    VTU files have a 0-indexing
    """
    _check_file(filename)

    vtk = import_optional_dependency("vtk")

    reader = vtk.vtkPolyDataReader()
    reader.SetFileName(filename)
    reader.Update()
    vtk_mesh = reader.GetOutput()

    vertices, faces = _dump_vtk(vtk_mesh)
    return Mesh(vertices, faces, name)
コード例 #2
0
def _build_vtkPolyData(vertices, faces):
    """Builds a vtkPolyData object from vertices and faces"""

    vtk = import_optional_dependency("vtk")

    # Create a vtkPoints object and store the points in it
    points = vtk.vtkPoints()
    for point in vertices:
        points.InsertNextPoint(point)

    # Create a vtkCellArray to store faces
    cell_array = vtk.vtkCellArray()
    for face_ids in faces:
        if face_ids[0] == face_ids[-1]:
            # Triangle
            curface = face_ids[:3]
            vtk_face = vtk.vtkTriangle()
        else:
            # Quadrangle
            curface = face_ids[:4]
            vtk_face = vtk.vtkQuad()

        for idx, id in enumerate(curface):
            vtk_face.GetPointIds().SetId(idx, id)

        cell_array.InsertNextCell(vtk_face)

    polydata_mesh = vtk.vtkPolyData()
    polydata_mesh.SetPoints(points)
    polydata_mesh.SetPolys(cell_array)

    return polydata_mesh
コード例 #3
0
def write_VTP(filename, vertices, faces):
    """Writes .vtp file format for the Paraview (Kitware (c)) visualisation software.

    It relies on the VTK library for its writer. VTP files use the last XML file format of the VTK library and
    correspond to polydata.

    Parameters
    ----------
    filename: str
        name of the mesh file to be written on disk
    vertices: ndarray
        numpy array of the coordinates of the mesh's nodes
    faces: ndarray
        numpy array of the faces' nodes connectivities
    """

    vtk = import_optional_dependency("vtk")

    writer = vtk.vtkXMLPolyDataWriter()
    writer.SetDataModeToAscii()
    writer.SetFileName(filename)

    polydata = _build_vtkPolyData(vertices, faces)
    writer.SetInputData(polydata)
    writer.Write()
コード例 #4
0
ファイル: mesh_loaders.py プロジェクト: mancellin/capytaine
def load_WRL(filename, name=None):
    """Loads VRML 2.0 mesh files.

    Parameters
    ----------
    filename: str
        name of the mesh file on disk

    Returns
    -------
    Mesh
        the loaded mesh
    """
    import re

    vtk = import_optional_dependency("vtk")

    _check_file(filename)

    # Checking version
    with open(filename, 'r') as f:
        line = f.readline()
        ver = re.search(r'#VRML\s+V(\d.\d)', line).group(1)
        if not ver == '2.0':
            raise NotImplementedError('VRML loader only supports VRML 2.0 format (version %s given)' % ver)

    importer = vtk.vtkVRMLImporter()
    importer.SetFileName(str(filename))
    importer.Update()

    actors = importer.GetRenderer().GetActors()
    actors.InitTraversal()
    dataset = actors.GetNextActor().GetMapper().GetInput()

    return _dump_vtk(dataset)
コード例 #5
0
    def plot_shape(self):
        """Plot the structure of the matrix using matplotlib."""
        matplotlib = import_optional_dependency("matplotlib")
        plt = matplotlib.pyplot

        plt.figure()
        for patch in self._patches((0, 0)):
            plt.gca().add_patch(patch)
        plt.axis('equal')
        plt.xlim(0, self.shape[1])
        plt.ylim(0, self.shape[0])
        plt.gca().invert_yaxis()
コード例 #6
0
def load_STL(filename, name=None):
    """Loads STL file format.

    It relies on the reader from the VTK library. As STL file format maintains a redundant set of vertices for each
    faces of the mesh, it returns a merged list of nodes and connectivity array by using the merge_duplicates function.

    Parameters
    ----------
    filename: str
        name of the meh file on disk

    Returns
    -------
    Mesh
        the loaded mesh

    Note
    ----
    STL files have a 0-indexing
    """
    vtk = import_optional_dependency("vtk")

    from capytaine.meshes.quality import merge_duplicate_rows

    _check_file(filename)

    reader = vtk.vtkSTLReader()
    reader.SetFileName(filename)
    reader.Update()

    data = reader.GetOutputDataObject(0)

    nv = data.GetNumberOfPoints()
    vertices = np.zeros((nv, 3), dtype=float)
    for k in range(nv):
        vertices[k] = np.array(data.GetPoint(k))
    nf = data.GetNumberOfCells()
    faces = np.zeros((nf, 4), dtype=int)
    for k in range(nf):
        cell = data.GetCell(k)
        if cell is not None:
            for l in range(3):
                faces[k][l] = cell.GetPointId(l)
                faces[k][3] = faces[k][
                    0]  # always repeating the first node as stl is triangle only

    # Merging duplicates nodes
    vertices, new_id = merge_duplicate_rows(vertices)
    faces = new_id[faces]

    return Mesh(vertices, faces, name)
コード例 #7
0
def _build_vtkUnstructuredGrid(vertices, faces):
    """Internal function that builds a VTK object for manipulation by the VTK library.

    Parameters
    ----------
    vertices: ndarray
        numpy array of the coordinates of the mesh's nodes
    faces: ndarray
        numpy array of the faces' nodes connectivities

    Returns
    -------
    vtkObject
    """

    vtk = import_optional_dependency("vtk")

    nv = max(np.shape(vertices))
    nf = max(np.shape(faces))

    vtk_mesh = vtk.vtkUnstructuredGrid()
    vtk_mesh.Allocate(nf, nf)

    # Building the vtkPoints data structure
    vtk_points = vtk.vtkPoints()
    vtk_points.SetNumberOfPoints(nv)
    for idx, vertex in enumerate(vertices):
        vtk_points.SetPoint(idx, vertex)

    vtk_mesh.SetPoints(vtk_points)  # Storing the points into vtk_mesh

    # Building the vtkCell data structure
    for cell in faces:
        if cell[-1] in cell[:-1]:
            vtk_cell = vtk.vtkTriangle()
            nc = 3
        else:
            # #print 'quadrangle'
            vtk_cell = vtk.vtkQuad()
            nc = 4

        for k in range(nc):
            vtk_cell.GetPointIds().SetId(k, cell[k])

        vtk_mesh.InsertNextCell(vtk_cell.GetCellType(), vtk_cell.GetPointIds())
    return vtk_mesh
コード例 #8
0
ファイル: meshes.py プロジェクト: ryancoe/capytaine
    def compute_quadrature(self, method):
        quadpy = import_optional_dependency("quadpy")
        transform = quadpy.ncube._helpers.transform
        get_detJ = quadpy.ncube._helpers.get_detJ

        if method is None:
            if 'quadrature' in self.__internals__:
                del self.__internals__['quadrature']
                del self.__internals__['quadrature_method']
            else:
                pass

        elif isinstance(method,
                        quadpy.quadrilateral._helpers.QuadrilateralScheme):
            points = np.empty((self.nb_faces, len(method.points), 3))
            weights = np.empty((self.nb_faces, len(method.points)))

            self.heal_triangles()

            for i_face in range(self.nb_faces):
                ref = self.vertices[self.faces[i_face, 0], :]
                A, B, C, D = self.vertices[self.faces[i_face, :], :] - ref
                n = self.faces_normals[i_face, :]

                ex = (B - A) / norm(B - A)
                ez = n / norm(n)
                ey = np.cross(ex, ez)
                R = np.array([ex, ey, ez])
                quadrilateral = np.array([[R @ A, R @ D], [R @ B,
                                                           R @ C]])[:, :, :2]

                quadpoints = transform(method.points.T, quadrilateral)
                quadpoints = np.concatenate(
                    [quadpoints,
                     np.zeros((quadpoints.shape[0], 1))], axis=1)
                quadpoints = np.array([R.T @ p for p in quadpoints]) + ref
                points[i_face, :, :] = quadpoints

                weights[i_face, :] = method.weights * abs(
                    get_detJ(method.points.T, quadrilateral))

            self.__internals__['quadrature'] = (points, weights)
            self.__internals__['quadrature_method'] = method

        else:
            raise NotImplementedError
コード例 #9
0
def write_VTU(filename, vertices, faces):
    """Writes .vtu file format for the paraview (Kitware (c)) visualisation software.

    It relies on the VTK library for its writer. VTU files use the last XML file format of the VTK library.

    Parameters
    ----------
    filename: str
        name of the mesh file to be written on disk
    vertices: ndarray
        numpy array of the coordinates of the mesh's nodes
    faces: ndarray
        numpy array of the faces' nodes connectivities
    """
    vtk = import_optional_dependency("vtk")

    writer = vtk.vtkXMLUnstructuredGridWriter()
    writer.SetDataModeToAscii()
    writer.SetFileName(filename)

    unstructured_grid = _build_vtkUnstructuredGrid(vertices, faces)
    writer.SetInputData(unstructured_grid)
    writer.Write()
コード例 #10
0
#!/usr/bin/env python
# coding: utf-8
"""Tools for 3D displays with VTK."""
# Copyright (C) 2017-2019 Matthieu Ancellin
# See LICENSE file at <https://github.com/mancellin/capytaine>

from typing import Union

from capytaine.meshes.meshes import Mesh
from capytaine.meshes.collections import CollectionOfMeshes
from capytaine.tools.optional_imports import import_optional_dependency

vtk = import_optional_dependency("vtk")

def compute_vtk_polydata(mesh: Union[Mesh, CollectionOfMeshes]):
    """Transform a mesh into vtkPolydata."""

    # Create a vtkPoints object and store the points in it
    points = vtk.vtkPoints()
    for point in mesh.vertices:
        points.InsertNextPoint(point)

    # Create a vtkCellArray to store faces
    faces = vtk.vtkCellArray()
    for face_ids in mesh.faces:
        if face_ids[0] == face_ids[-1]:
            # Triangle
            curface = face_ids[:3]
            vtk_face = vtk.vtkTriangle()
        else:
            # Quadrangle
コード例 #11
0
ファイル: meshes.py プロジェクト: ryancoe/capytaine
    def show_matplotlib(self,
                        ax=None,
                        normal_vectors=False,
                        scale_normal_vector=None,
                        saveas=None,
                        **kwargs):
        """Poor man's viewer with matplotlib.

        Parameters
        ----------
        ax: matplotlib axis
            The 3d axis in which to plot the mesh. If not provided, create a new one.
        normal_vector: bool
            If True, print normal vector.
        scale_normal_vector: array of shape (nb_faces, )
            Scale separately each of the normal vectors.
        saveas: str
            file path where to save the image

        Other parameters are passed to Poly3DCollection.
        """
        matplotlib = import_optional_dependency("matplotlib")
        plt = matplotlib.pyplot

        mpl_toolkits = import_optional_dependency("mpl_toolkits",
                                                  package_name="matplotlib")
        Poly3DCollection = mpl_toolkits.mplot3d.art3d.Poly3DCollection

        default_axis = ax is None
        if default_axis:
            fig = plt.figure()
            ax = fig.add_subplot(111, projection="3d")

        faces = []
        for face in self.faces:
            vertices = []
            for index_vertex in face:
                vertices.append(self.vertices[int(index_vertex), :])
            faces.append(vertices)
        if 'facecolors' not in kwargs:
            kwargs['facecolors'] = (0.3, 0.3, 0.3, 0.3)
        if 'edgecolor' not in kwargs:
            kwargs['edgecolor'] = 'k'
        ax.add_collection3d(Poly3DCollection(faces, **kwargs))

        # Plot normal vectors.
        if normal_vectors:
            if scale_normal_vector is not None:
                vectors = (scale_normal_vector * self.faces_normals.T).T
            else:
                vectors = self.faces_normals
            ax.quiver(*zip(*self.faces_centers), *zip(*vectors), length=0.2)

        if default_axis:
            ax.set_xlabel("x")
            ax.set_ylabel("y")

            xmin, xmax, ymin, ymax, zmin, zmax = self.squared_axis_aligned_bbox
            ax.set_xlim(xmin, xmax)
            ax.set_ylim(ymin, ymax)
            ax.set_zlim(zmin, zmax)

            if saveas is not None:
                plt.tight_layout()
                plt.savefig(saveas)
            else:
                plt.show()
コード例 #12
0
    def _patches(self,
                 global_frame: Union[Tuple[int, int], np.ndarray]
                 ):
        """Helper function for displaying the shape of the matrix.
        Recursively returns a list of rectangles representing the sub-blocks of the matrix.

        Uses BlockMatrix.display_color to assign color to the blocks.
        By default, it cycles through matplotlib default colors.
        But if display_color is redefined as a callable, it is called with the block as argument.

        Parameters
        ----------
        global_frame: tuple of ints
            coordinates of the origin in the top left corner.

        Returns
        -------
        list of matplotlib.patches.Rectangle
        """
        matplotlib_patches = import_optional_dependency("matplotlib.patches", "matplotlib")
        Rectangle = matplotlib_patches.Rectangle

        all_blocks_in_flat_iterator = (block for line in self._stored_blocks for block in line)
        positions_of_all_blocks = self._stored_block_positions(global_frame=global_frame)
        patches = []
        for block, positions_of_the_block in zip(all_blocks_in_flat_iterator, positions_of_all_blocks):
            position_of_first_appearance = positions_of_the_block[0]
            # Exchange coordinates: row index i -> y, column index j -> x
            position_of_first_appearance = np.array((position_of_first_appearance[1], position_of_first_appearance[0]))

            if isinstance(block, BlockMatrix):
                patches_of_this_block = block._patches(np.array((position_of_first_appearance[1], position_of_first_appearance[0])))
            elif isinstance(block, np.ndarray):

                if isinstance(self.display_color, Iterator):
                    color = next(self.display_color)
                elif callable(self.display_color):
                    color = self.display_color(block)
                else:
                    color = np.random.rand(3)

                patches_of_this_block = [Rectangle(position_of_first_appearance,
                                                   block.shape[1], block.shape[0],
                                                   edgecolor='k', facecolor=color)]
            elif isinstance(block, LowRankMatrix):

                if isinstance(self.display_color, Iterator):
                    color = next(self.display_color)
                elif callable(self.display_color):
                    color = self.display_color(block)
                else:
                    color = np.random.rand(3)

                patches_of_this_block = [
                    # Left block
                    Rectangle(position_of_first_appearance,
                              block.left_matrix.shape[1], block.left_matrix.shape[0],
                              edgecolor='k', facecolor=color),
                    # Top block
                    Rectangle(position_of_first_appearance,
                              block.right_matrix.shape[1], block.right_matrix.shape[0],
                              edgecolor='k', facecolor=color),
                    # Rest of the matrix
                    Rectangle(position_of_first_appearance,
                              block.right_matrix.shape[1], block.left_matrix.shape[0],
                              facecolor=color, alpha=0.2),
                ]
            else:
                raise NotImplementedError()

            patches.extend(patches_of_this_block)

            # For the other appearances, copy the patches of the first appearance
            for block_position in positions_of_the_block[1:]:
                block_position = np.array((block_position[1], block_position[0]))
                for patch in patches_of_this_block:  # A block can be made of several patches.
                    shift = block_position - position_of_first_appearance
                    patch_position = np.array(patch.get_xy()) + shift
                    patches.append(Rectangle(patch_position, patch.get_width(), patch.get_height(),
                                             facecolor=patch.get_facecolor(), alpha=0.2))

        return patches
コード例 #13
0
ファイル: meshes.py プロジェクト: mancellin/capytaine
    def show_matplotlib(self,
                        ax=None,
                        normal_vectors=False,
                        scale_normal_vector=None,
                        saveas=None,
                        color_field=None,
                        cmap=None,
                        cbar_label=None,
                        **kwargs):
        """Poor man's viewer with matplotlib.

        Parameters
        ----------
        ax: matplotlib axis
            The 3d axis in which to plot the mesh. If not provided, create a new one.
        normal_vectors: bool
            If True, print normal vector.
        scale_normal_vector: array of shape (nb_faces, )
            Scale separately each of the normal vectors.
        saveas: str
            File path where to save the image.
        color_field: array of shape (nb_faces, )
            Scalar field to be plot on the mesh (optional).
        cmap: matplotlib colormap
            Colormap to use for field plotting.
        cbar_label: string
            Label for colormap

        Other parameters are passed to Poly3DCollection.
        """
        matplotlib = import_optional_dependency("matplotlib")
        plt = matplotlib.pyplot
        cm = matplotlib.cm

        mpl_toolkits = import_optional_dependency("mpl_toolkits",
                                                  package_name="matplotlib")
        Poly3DCollection = mpl_toolkits.mplot3d.art3d.Poly3DCollection

        default_axis = ax is None
        if default_axis:
            fig = plt.figure()
            ax = fig.add_subplot(111, projection="3d")

        faces = []
        for face in self.faces:
            vertices = []
            for index_vertex in face:
                vertices.append(self.vertices[int(index_vertex), :])
            faces.append(vertices)

        if color_field is None:
            if 'facecolors' not in kwargs:
                kwargs['facecolors'] = "yellow"
        else:
            if cmap is None:
                cmap = cm.get_cmap('coolwarm')
            m = cm.ScalarMappable(cmap=cmap)
            m.set_array([min(color_field), max(color_field)])
            m.set_clim(vmin=min(color_field), vmax=max(color_field))
            colors = m.to_rgba(color_field)
            kwargs['facecolors'] = colors
        if 'edgecolor' not in kwargs:
            kwargs['edgecolor'] = 'k'

        ax.add_collection3d(Poly3DCollection(faces, **kwargs))

        if color_field is not None:
            cbar = plt.colorbar(m)
            if cbar_label is not None:
                cbar.set_label(cbar_label)

        # Plot normal vectors.
        if normal_vectors:
            if scale_normal_vector is not None:
                vectors = (scale_normal_vector * self.faces_normals.T).T
            else:
                vectors = self.faces_normals
            ax.quiver(*zip(*self.faces_centers), *zip(*vectors), length=0.2)

        ax.set_xlabel("x")
        ax.set_ylabel("y")
        ax.set_zlabel("z")

        xmin, xmax, ymin, ymax, zmin, zmax = self.squared_axis_aligned_bbox
        ax.set_xlim(xmin, xmax)
        ax.set_ylim(ymin, ymax)
        ax.set_zlim(zmin, zmax)

        if default_axis:
            if saveas is not None:
                plt.tight_layout()
                plt.savefig(saveas)
            else:
                plt.show()
コード例 #14
0
ファイル: meshes.py プロジェクト: mancellin/capytaine
    def compute_quadrature(self, method):
        quadpy = import_optional_dependency("quadpy")
        transform = quadpy.c2.transform
        get_detJ = quadpy.cn._helpers.get_detJ

        if method is None:
            # No quadrature (i.e. default first order quadrature)
            if 'quadrature' in self.__internals__:
                del self.__internals__['quadrature']
                del self.__internals__['quadrature_method']
            else:
                pass

        elif isinstance(method, quadpy.c2._helpers.C2Scheme):
            assert method.points.shape[0] == method.dim == 2
            nb_points = method.points.shape[1]
            points = np.empty((self.nb_faces, nb_points, 3))
            weights = np.empty((self.nb_faces, nb_points))

            self.heal_triangles()

            for i_face in range(self.nb_faces):
                # Define a local frame (Oxyz) such that
                # * the corner A of the quadrilateral panel is the origin of the local frame
                # * the edge AB of the quadrilateral panel is along the local x-axis,
                # * the quadrilateral panel is within the local xy-plane (that is, its normal is along the local z-axis).
                # Hence, the corners of the panels all have 0 as z-coordinate in the local frame.

                # Coordinates in global frame
                global_A, global_B, global_C, global_D = self.vertices[
                    self.faces[i_face, :], :]
                n = self.faces_normals[i_face, :]

                ex = (global_B - global_A) / norm(
                    global_B - global_A)  # unit vector of the local x-axis
                ez = n / norm(n)  # unit vector of the local z-axis
                ey = np.cross(
                    ex, ez
                )  # unit vector of the local y-axis, such that the basis is orthonormal

                R = np.array([ex, ey, ez])
                local_A = np.zeros(
                    (3, )
                )  # coordinates of A in local frame, should be zero by construction
                local_B = R @ (global_B - global_A
                               )  # coordinates of B in local frame
                local_C = R @ (global_C - global_A
                               )  # coordinates of C in local frame
                local_D = R @ (global_D - global_A
                               )  # coordinates of D in local frame

                local_quadrilateral = np.array([[local_A, local_D],
                                                [local_B, local_C]])[:, :, :-1]
                # Removing last index in last dimension because not interested in z-coordinate which is 0.

                local_quadpoints = transform(method.points,
                                             local_quadrilateral)

                local_quadpoints_in_3d = np.concatenate(
                    [local_quadpoints,
                     np.zeros((nb_points, 1))], axis=1)
                global_quadpoints = np.array(
                    [R.T @ p for p in local_quadpoints_in_3d]) + global_A
                points[i_face, :, :] = global_quadpoints

                weights[i_face, :] = method.weights * 4 * np.abs(
                    get_detJ(method.points, local_quadrilateral))

            self.__internals__['quadrature'] = (points, weights)
            self.__internals__['quadrature_method'] = method

        else:
            raise NotImplementedError