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())
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)
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
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
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())
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))
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())
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)
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))
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()
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
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
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
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
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)
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 )
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)
- `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)
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)
def plot(d): data = pv.wrap(d) data.plot(volume=True)
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())
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
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
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
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
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