def sphere(self, center, color, scale, opacity=1.0, resolution=8, backface_culling=False, radius=None): factor = 1.0 if radius is not None else scale with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) sphere = vtk.vtkSphereSource() sphere.SetThetaResolution(resolution) sphere.SetPhiResolution(resolution) if radius is not None: sphere.SetRadius(radius) sphere.Update() geom = sphere.GetOutput() mesh = PolyData(np.array(center)) glyph = mesh.glyph(orient=False, scale=False, factor=factor, geom=geom) actor = _add_mesh(self.plotter, mesh=glyph, color=color, opacity=opacity, backface_culling=backface_culling, smooth_shading=self.figure.smooth_shading) return actor, glyph
def contour(self, surface, scalars, contours, line_width=1.0, opacity=1.0, vmin=None, vmax=None, colormap=None, normalized_colormap=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) from pyvista import PolyData cmap = _get_colormap_from_array(colormap, normalized_colormap) vertices = np.array(surface['rr']) triangles = np.array(surface['tris']) n_triangles = len(triangles) triangles = np.c_[np.full(n_triangles, 3), triangles] pd = PolyData(vertices, triangles) pd.point_arrays['scalars'] = scalars self.plotter.add_mesh(pd.contour(isosurfaces=contours, rng=(vmin, vmax)), show_scalar_bar=False, line_width=line_width, cmap=cmap, opacity=opacity, smooth_shading=self.figure.smooth_shading)
def sphere(self, center, color, scale, opacity=1.0, resolution=8, backface_culling=False, radius=None): factor = 1.0 if radius is not None else scale center = np.array(center, dtype=float) if len(center) == 0: return None, None _check_option('center.ndim', center.ndim, (1, 2)) _check_option('center.shape[-1]', center.shape[-1], (3,)) with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) sphere = vtk.vtkSphereSource() sphere.SetThetaResolution(resolution) sphere.SetPhiResolution(resolution) if radius is not None: sphere.SetRadius(radius) sphere.Update() geom = sphere.GetOutput() mesh = PolyData(center) glyph = mesh.glyph(orient=False, scale=False, factor=factor, geom=geom) actor = _add_mesh( self.plotter, mesh=glyph, color=color, opacity=opacity, backface_culling=backface_culling, smooth_shading=self.smooth_shading ) return actor, glyph
def surface(self, surface, color=None, opacity=1.0, vmin=None, vmax=None, colormap=None, normalized_colormap=False, scalars=None, backface_culling=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) cmap = _get_colormap_from_array(colormap, normalized_colormap) vertices = np.array(surface['rr']) triangles = np.array(surface['tris']) n_triangles = len(triangles) triangles = np.c_[np.full(n_triangles, 3), triangles] mesh = PolyData(vertices, triangles) if scalars is not None: mesh.point_arrays['scalars'] = scalars self.plotter.add_mesh(mesh=mesh, color=color, rng=[vmin, vmax], show_scalar_bar=False, opacity=opacity, cmap=cmap, backface_culling=backface_culling, smooth_shading=self.figure.smooth_shading)
def contour(self, surface, scalars, contours, width=1.0, opacity=1.0, vmin=None, vmax=None, colormap=None, normalized_colormap=False, kind='line', color=None): with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) if colormap is not None: colormap = _get_colormap_from_array(colormap, normalized_colormap) vertices = np.array(surface['rr']) triangles = np.array(surface['tris']) n_triangles = len(triangles) triangles = np.c_[np.full(n_triangles, 3), triangles] mesh = PolyData(vertices, triangles) mesh.point_arrays['scalars'] = scalars contour = mesh.contour(isosurfaces=contours, rng=(vmin, vmax)) line_width = width if kind == 'tube': contour = contour.tube(radius=width) line_width = 1.0 self.plotter.add_mesh(mesh=contour, show_scalar_bar=False, line_width=line_width, color=color, cmap=colormap, opacity=opacity, smooth_shading=self.figure.smooth_shading)
def surface(self, surface, color=None, opacity=1.0, vmin=None, vmax=None, colormap=None, normalized_colormap=False, scalars=None, backface_culling=False, polygon_offset=None): with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) normals = surface.get('nn', None) vertices = np.array(surface['rr']) triangles = np.array(surface['tris']) triangles = np.c_[np.full(len(triangles), 3), triangles] mesh = PolyData(vertices, triangles) colormap = _get_colormap_from_array(colormap, normalized_colormap) if scalars is not None: mesh.point_arrays['scalars'] = scalars return self.polydata( mesh=mesh, color=color, opacity=opacity, normals=normals, backface_culling=backface_culling, scalars=scalars, colormap=colormap, vmin=vmin, vmax=vmax, polygon_offset=polygon_offset, )
def mono_centering(data): if VERBOSE: print(f"Centering: {data['meta_data']['name']}") mesh = data["poly_data"] remesh = PolyData(mesh.points.copy(), mesh.faces.copy()) offset = mesh.center remesh.translate(np.zeros_like(offset) - offset) return remesh
def __init__( self, mesh: pv.PolyData, meridian: float, offset: Optional[float] = None, ): """ TODO Parameters ---------- mesh meridian offset Notes ----- .. versionadded :: 0.1.0 """ # logging convenience self._extra = dict(cls=self.__class__.__name__) if is_projected(mesh): emsg = "Cannot slice mesh that appears to be a planar projection." raise ValueError(emsg) self._info = mesh.active_scalars_info mesh[GV_CELL_IDS] = np.arange(mesh.n_cells) mesh[GV_POINT_IDS] = np.arange(mesh.n_points) mesh.set_active_scalars( self._info.name, preference=self._info.association.name.lower() ) self.mesh = mesh # XXX: hack self.radius = calculate_radius(mesh, decimals=6) self.meridian = wrap(meridian)[0] self.offset = abs(CUT_OFFSET if offset is None else offset) logger.debug( "meridian=%s, offset=%s, radius=%s", self.meridian, self.offset, self.radius, extra=self._extra, ) self.slices = {bias.name: self._intersection(bias.value) for bias in SliceBias} n_cells = self.slices[CUT_EXACT].n_cells self.west_ids = set(self.slices[CUT_WEST][GV_CELL_IDS]) if n_cells else set() self.east_ids = set(self.slices[CUT_EAST][GV_CELL_IDS]) if n_cells else set() self.split_ids = self.west_ids.intersection(self.east_ids) logger.debug( "west=%s, east=%s, split=%s", len(self.west_ids), len(self.east_ids), len(self.split_ids), extra=self._extra, )
def center(self): if VERBOSE: print("Centering") tmp_mesh = [] for mesh, full_mesh_stuff in zip(self.history[-1], self.full_data): remesh = PolyData(mesh.points.copy(), mesh.faces.copy()) offset = mesh.center remesh.translate(np.zeros_like(offset) - offset) tmp_mesh.append(remesh) self.history.append(tmp_mesh)
def Sphere(radius=0.5, center=(0, 0, 0), direction=(0, 0, 1), theta_resolution=30, phi_resolution=30, start_theta=0, end_theta=360, start_phi=0, end_phi=180): """ Create a vtk Sphere Parameters ---------- radius : float, optional Sphere radius center : np.ndarray or list, optional Center in [x, y, z] direction : list or np.ndarray Direction the top of the sphere points to in [x, y, z] theta_resolution: int , optional Set the number of points in the longitude direction (ranging from start_theta to end theta). phi_resolution : int, optional Set the number of points in the latitude direction (ranging from start_phi to end_phi). start_theta : float, optional Starting longitude angle. end_theta : float, optional Ending longitude angle. start_phi : float, optional Starting latitude angle. end_phi : float, optional Ending latitude angle. Returns ------- sphere : pyvista.PolyData Sphere mesh. """ sphere = vtk.vtkSphereSource() sphere.SetRadius(radius) sphere.SetThetaResolution(theta_resolution) sphere.SetPhiResolution(phi_resolution) sphere.SetStartTheta(start_theta) sphere.SetEndTheta(end_theta) sphere.SetStartPhi(start_phi) sphere.SetEndPhi(end_phi) sphere.Update() surf = PolyData(sphere.GetOutput()) surf.rotate_y(-90) translate(surf, center, direction) return surf
def add_texture_coords( mesh: pv.PolyData, meridian: Optional[float] = None, antimeridian: Optional[bool] = False, ) -> pv.PolyData: """ TODO Parameters ---------- mesh meridian antimeridian inplace Returns ------- Notes ----- .. versionadded:: 0.1.0 """ if meridian is None: meridian = DEFAULT_MERIDIAN if antimeridian: meridian += 180 meridian = wrap(meridian)[0] if GV_REMESH_POINT_IDS not in mesh.point_data: mesh = cut_along_meridian(mesh, meridian=meridian) else: mesh = mesh.copy(deep=True) # convert from cartesian xyz to spherical lat/lons ll = to_xy0(mesh, closed_interval=True) lons, lats = ll[:, 0], ll[:, 1] # convert to normalised UV space u = (lons + 180) / 360 v = (lats + 90) / 180 t = np.vstack([u, v]).T mesh.active_t_coords = t logger.debug( "u.min()=%s, u.max()=%s, v.min()=%s, v.max()=%s", u.min(), u.max(), v.min(), v.max(), ) return mesh
def Cylinder(center=(0., 0., 0.), direction=(1., 0., 0.), radius=0.5, height=1.0, resolution=100, **kwargs): """ Create the surface of a cylinder. Parameters ---------- center : list or np.ndarray Location of the centroid in [x, y, z] direction : list or np.ndarray Direction cylinder points to in [x, y, z] radius : float Radius of the cylinder. height : float Height of the cylinder. resolution : int Number of points on the circular face of the cylinder. capping : bool, optional Cap cylinder ends with polygons. Default True Returns ------- cylinder : pyvista.PolyData Cylinder surface. Examples -------- >>> import pyvista >>> import numpy as np >>> cylinder = pyvista.Cylinder(np.array([1, 2, 3]), np.array([1, 1, 1]), 1, 1) >>> cylinder.plot() # doctest:+SKIP """ capping = kwargs.get('capping', kwargs.get('cap_ends', True)) cylinderSource = vtk.vtkCylinderSource() cylinderSource.SetRadius(radius) cylinderSource.SetHeight(height) cylinderSource.SetCapping(capping) cylinderSource.SetResolution(resolution) cylinderSource.Update() surf = PolyData(cylinderSource.GetOutput()) surf.rotate_z(-90) translate(surf, center, direction) return surf
def clip_horizon_with_faults( self, horizon: pv.PolyData, faults: Iterable[pv.PolyData], value: float = None ) -> List[pv.PolyData]: """Clip given horizon surface with given list of fault surfaces. The given value represents the distance to clip away from the fault surfaces. Args: horizon (pv.PolyData): The horizon surface to be clipped. faults (Iterable[pv.PolyData]): Fault(s) surface(s) to clip with. value (float, optional): Set the clipping value of the implicit function (clipping distance from faults). Defaults to 50. Returns: List[pv.PolyData]: Individual clipped horizon surfaces. """ if hasattr(faults, "next"): if type(faults[0]) == str: faults = [self.get_surface(f) for f in faults] horizons = [] if not value: value = np.mean(self.model.grid.regular_grid.get_dx_dy_dz()[:2]) # TODO: this somehow doesn't work properly with Gullfaks model horizons.append( horizon.clip_surface(faults[0], value=-value) ) horizons.append( horizon.clip_surface(faults[-1], invert=False, value=-value) ) if len(faults) == 1: print("Returning after 1") return horizons for f1, f2 in zip(faults[:-1], faults[1:]): horizons.append( horizon.clip_surface( f1, invert=False, value=value ).clip_surface( f2, value=-value ) ) return horizons
def process_spider_box_unit_cell( spider: pv.PolyData = get_unit_cell_spider(), box: pv.PolyData = get_unit_cell_box(), scale: float = 1.0, rotation: List[Tuple[str, float]] = None, translation: List[Union[int, float]] = None, ) -> Tuple[pv.PolyData, pv.PolyData]: """Process the spider-box unit cell through operations including scaling, rotations, and translations. Args: spider (pv.PolyData, optional): Polydata containing the spider unit. Defaults to get_unit_cell_spider(). box (pv.PolyData, optional): Polydata containing the box unit. Defaults to get_unit_cell_box(). scale (float, optional): scaling factor. Defaults to 1.0. rotation (List[Tuple[str, float]], optional): list of steps for rotation, in the form of list of tuples, and the tuple containing the direction (``"x"``, ``"y"``, or ``"z"``) in the first element, and the degrees in the second direction. Example: ``[("x", 90), ("z", 180)]``. Under the hood, the `rotate_x <https://docs.pyvista.org/core/common.html#pyvista.Common.rotate_x>`_, `rotate_y <https://docs.pyvista.org/core/common.html#pyvista.Common.rotate_y>`_, and `rotate_z <https://docs.pyvista.org/core/common.html#pyvista.Common.rotate_z>`_ methods in ``pv.PolyData`` are called. Defaults to None. translation (List[Union[int, float]], optional): Length of 3 list or array to translate the polydata. Under the hood, the `translate <https://docs.pyvista.org/core/common.html#pyvista.Common.translate>`_ method in ``pv.PolyData`` is called. Defaults to None. Returns: Tuple[pv.PolyData, pv.PolyData]: A tuple of ``pv.Polydata`` containing the spider and box. """ spider.points *= scale box.points *= scale if isinstance(rotation, list): for step in rotation: if step[0] == "x": spider.rotate_x(step[1]) if step[0] == "y": spider.rotate_y(step[1]) if step[0] == "z": spider.rotate_z(step[1]) if isinstance(translation, list): spider.translate(translation) box.translate(translation) return (spider, box)
def test_multi_block_clean(rectilinear, uniform, ant): # now test a clean of the null values multi = MultiBlock() multi[1, 'rect'] = rectilinear multi[2, 'empty'] = PolyData() multi[3, 'mempty'] = MultiBlock() multi[5, 'uni'] = uniform # perform the clean to remove all Null elements multi.clean() assert multi.n_blocks == 2 assert multi.GetNumberOfBlocks() == 2 assert isinstance(multi[0], RectilinearGrid) assert isinstance(multi[1], UniformGrid) assert multi.get_block_name(0) == 'rect' assert multi.get_block_name(1) == 'uni' # Test a nested data struct foo = MultiBlock() foo[3] = ant assert foo.n_blocks == 4 multi = MultiBlock() multi[1, 'rect'] = rectilinear multi[5, 'multi'] = foo # perform the clean to remove all Null elements assert multi.n_blocks == 6 multi.clean() assert multi.n_blocks == 2 assert multi.GetNumberOfBlocks() == 2 assert isinstance(multi[0], RectilinearGrid) assert isinstance(multi[1], MultiBlock) assert multi.get_block_name(0) == 'rect' assert multi.get_block_name(1) == 'multi' assert foo.n_blocks == 1
def sphere(self, center, color, scale, opacity=1.0, resolution=8, backface_culling=False): with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) from pyvista import PolyData sphere = vtk.vtkSphereSource() sphere.SetThetaResolution(resolution) sphere.SetPhiResolution(resolution) sphere.Update() geom = sphere.GetOutput() pd = PolyData(center) self.plotter.add_mesh(pd.glyph(orient=False, scale=False, factor=scale, geom=geom), color=color, opacity=opacity, backface_culling=backface_culling, smooth_shading=self.figure.smooth_shading)
def resize(mesh: pv.PolyData, radius: Optional[float] = None) -> pv.PolyData: """ Change the radius of the provided mesh. Parameters ---------- mesh : PolyData The mesh to be resized to the provided ``radius``. radius : float, default=1.0 The target radius of the ``mesh``. Returns ------- PolyData The resized mesh. Notes ----- .. versionadded:: 0.1.0 """ if is_projected(mesh): emsg = "Cannot resize mesh that appears to be a planar projection." raise ValueError(emsg) if radius is None: radius = 1.0 if radius and not np.isclose(calculate_radius(mesh), radius): lonlat = to_xy0(mesh) xyz = to_xyz(lonlat[:, 0], lonlat[:, 1], radius=radius) mesh.points = xyz return mesh
def mesh(self, x, y, z, triangles, color, opacity=1.0, shading=False, backface_culling=False, scalars=None, colormap=None, vmin=None, vmax=None, **kwargs): with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) from pyvista import PolyData smooth_shading = self.figure.smooth_shading vertices = np.c_[x, y, z] n_vertices = len(vertices) triangles = np.c_[np.full(len(triangles), 3), triangles] pd = PolyData(vertices, triangles) rgba = False if color is not None and len(color) == n_vertices: if color.shape[1] == 3: scalars = np.c_[color, np.ones(n_vertices)] else: scalars = color scalars = (scalars * 255).astype('ubyte') color = None # Disabling normal computation for smooth shading # is a temporary workaround of: # https://github.com/pyvista/pyvista-support/issues/15 smooth_shading = False rgba = True if isinstance(colormap, np.ndarray): if colormap.dtype == np.uint8: colormap = colormap.astype(np.float) / 255. from matplotlib.colors import ListedColormap colormap = ListedColormap(colormap) self.plotter.add_mesh(mesh=pd, color=color, scalars=scalars, rgba=rgba, opacity=opacity, cmap=colormap, backface_culling=backface_culling, rng=[vmin, vmax], show_scalar_bar=False, smooth_shading=smooth_shading)
def mesh(self, x, y, z, triangles, color, opacity=1.0, shading=False, backface_culling=False, scalars=None, colormap=None, vmin=None, vmax=None, interpolate_before_map=True, representation='surface', line_width=1., normals=None, polygon_offset=None, **kwargs): with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) vertices = np.c_[x, y, z] triangles = np.c_[np.full(len(triangles), 3), triangles] mesh = PolyData(vertices, triangles) return self.polydata( mesh=mesh, color=color, opacity=opacity, normals=normals, backface_culling=backface_culling, scalars=scalars, colormap=colormap, vmin=vmin, vmax=vmax, interpolate_before_map=interpolate_before_map, representation=representation, line_width=line_width, polygon_offset=polygon_offset, **kwargs, )
def Plane(center=(0, 0, 0), direction=(0, 0, 1), i_size=1, j_size=1, i_resolution=10, j_resolution=10): """ Create a plane Parameters ---------- center : list or np.ndarray Location of the centroid in [x, y, z] direction : list or np.ndarray Direction cylinder points to in [x, y, z] i_size : float Size of the plane in the i direction. j_size : float Size of the plane in the i direction. i_resolution : int Number of points on the plane in the i direction. j_resolution : int Number of points on the plane in the j direction. Returns ------- plane : pyvista.PolyData Plane mesh """ planeSource = vtk.vtkPlaneSource() planeSource.SetXResolution(i_resolution) planeSource.SetYResolution(j_resolution) planeSource.Update() surf = PolyData(planeSource.GetOutput()) surf.points[:, 0] *= i_size surf.points[:, 1] *= j_size surf.rotate_y(-90) translate(surf, center, direction) return surf
def mono_flipping(data): if VERBOSE: print(f"Flipping: {data['meta_data']['name']}") mesh = data["poly_data"] face_centers = mesh.cell_centers().points overall_signs = np.sum(np.sign(face_centers) * np.square(face_centers), axis=0) flipped_points = np.sign(overall_signs) * mesh.points remesh = PolyData(flipped_points, mesh.faces.copy()) return remesh
def _compute_curvature(surface: pv.PolyData) -> Curvature: """Private helper function to retrieve curvature based on Polydata computed curvature values for each vertex Args: surface: the mesh created by the faces and vertices by running marching cubes Returns: A cached Curvature object for each vertex """ mean_curvature = np.array(surface.curvature("Mean")) gaussian_curvature = np.array(surface.curvature("Gaussian")) p_min = np.array(surface.curvature("Minimum")) p_max = np.array(surface.curvature("Maximum")) _curvature = Curvature(gaussian_curvature=gaussian_curvature, mean_curvature=mean_curvature, principal_curvature_min=p_min, principal_curvature_max=p_max) return _curvature
def mono_scaling(data): if VERBOSE: print(f"\nScaling: {data['meta_data']['name']}") mesh = data["poly_data"] max_range = np.max(mesh.points, axis=0) min_range = np.min(mesh.points, axis=0) lengths_range = max_range - min_range longest_range = np.max(lengths_range) scaled_points = (mesh.points - min_range) / longest_range remesh = PolyData(scaled_points, mesh.faces.copy()) return remesh
def mono_alignment(data): if VERBOSE: print(f"Aligning: {data['meta_data']['name']}") mesh = data["poly_data"] A_cov = np.cov(mesh.points.T) eigenvalues, eigenvectors = np.linalg.eig(A_cov) biggest_idx = np.argsort(-eigenvalues) biggest_vec = eigenvectors[:, biggest_idx] new_points = np.dot(mesh.points, biggest_vec) remesh = PolyData(new_points, mesh.faces.copy()) return remesh
def mesh(self, x, y, z, triangles, color, opacity=1.0, shading=False, backface_culling=False, scalars=None, colormap=None, vmin=None, vmax=None, interpolate_before_map=True, representation='surface', line_width=1., normals=None, **kwargs): with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) vertices = np.c_[x, y, z] triangles = np.c_[np.full(len(triangles), 3), triangles] mesh = PolyData(vertices, triangles) if normals is not None: mesh.point_arrays["Normals"] = normals mesh.GetPointData().SetActiveNormals("Normals") return self._mesh( mesh, color, opacity, backface_culling, scalars, colormap, vmin, vmax, interpolate_before_map, representation, line_width, **kwargs, )
def plot_mesh_pyvista( plotter: pv.Plotter, polydata: pv.PolyData, # vertices: np.ndarray, # triangles: np.ndarray, rotations: List[Tuple[int, int, int]] = [(0, 0, 0)], vertexcolors: List[int] = [], vertexscalar: str = '', cmap: str = 'YlGnBu', title: str = '', scalar_bar_idx: int = 0, **mesh_kwargs, ): shape = plotter.shape if len(shape) == 1: assert shape[0] > 0 assert shape[0] == len(rotations) subp_idx = [(x, ) for x in range(shape[0])] else: assert shape[0] > 0 and shape[1] > 0 assert shape[0] * shape[1] == len(rotations) subp_idx = product(range(shape[0]), range(shape[1])) if vertexscalar and vertexcolors is not None: polydata[vertexscalar] = vertexcolors cmap = plt.cm.get_cmap(cmap) mesh_kwargs = { 'cmap': cmap, 'flip_scalars': True, 'show_scalar_bar': False, **mesh_kwargs, } if vertexscalar and vertexcolors is not None: mesh_kwargs['scalars'] = vertexscalar for i, (subp, rots) in enumerate(zip(subp_idx, rotations)): x, y, z = rots plotter.subplot(*subp) poly_copy = polydata.copy() poly_copy.rotate_x(x) poly_copy.rotate_y(y) poly_copy.rotate_z(z) plotter.add_mesh( poly_copy, **mesh_kwargs, ) if i == 0: plotter.add_title(title, font_size=5) if i == scalar_bar_idx: plotter.add_scalar_bar(label_font_size=10, position_x=0.85)
def get_pyvista_mesh(self, vertices: np.ndarray, triangles: np.ndarray): """Creates a PyVista mesh from the data. """ try: from pyvista import PolyData triangles = np.hstack( np.append(np.full((triangles.shape[0], 1), 3), triangles, axis=1)) return PolyData(vertices, triangles) except ImportError: raise ImportError( "The 'get_pyvista_mesh' method requires the PyVista python-package to" " be installed. Consider installing it via 'pip install pyvista'." )
def Arrow(start=(0., 0., 0.), direction=(1., 0., 0.), tip_length=0.25, tip_radius=0.1, shaft_radius=0.05, shaft_resolution=20): """ Create a vtk Arrow Parameters ---------- start : np.ndarray Start location in [x, y, z] direction : list or np.ndarray Direction the arrow points to in [x, y, z] tip_length : float, optional Length of the tip. tip_radius : float, optional Radius of the tip. shaft_radius : float, optional Radius of the shaft. shaft_resolution : int, optional Number of faces around the shaft Returns ------- arrow : pyvista.PolyData Arrow surface. """ # Create arrow object arrow = vtk.vtkArrowSource() arrow.SetTipLength(tip_length) arrow.SetTipRadius(tip_radius) arrow.SetShaftRadius(shaft_radius) arrow.SetShaftResolution(shaft_resolution) arrow.Update() surf = PolyData(arrow.GetOutput()) translate(surf, start, direction) return surf
def mesh(self, x, y, z, triangles, color, opacity=1.0, shading=False, backface_culling=False, **kwargs): with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) from pyvista import PolyData smooth_shading = self.figure.smooth_shading vertices = np.c_[x, y, z] n_vertices = len(vertices) triangles = np.c_[np.full(len(triangles), 3), triangles] pd = PolyData(vertices, triangles) if len(color) == n_vertices: if color.shape[1] == 3: scalars = np.c_[color, np.ones(n_vertices)] else: scalars = color scalars = (scalars * 255).astype('ubyte') color = None # Disabling normal computation for smooth shading # is a temporary workaround of: # https://github.com/pyvista/pyvista-support/issues/15 smooth_shading = False rgba = True else: scalars = None rgba = False self.plotter.add_mesh(mesh=pd, color=color, scalars=scalars, rgba=rgba, opacity=opacity, backface_culling=backface_culling, smooth_shading=smooth_shading)
def cut_along_meridian( mesh: pv.PolyData, meridian: Optional[float] = None, antimeridian: Optional[bool] = False, ) -> pv.PolyData: """ TODO Parameters ---------- mesh meridian antimeridian Returns ------- Notes ----- .. versionadded:: 0.1.0 """ if not isinstance(mesh, pv.PolyData): emsg = f"Require a 'pyvista.PolyData' mesh, got '{mesh.__class__.__name__}'." raise TypeError(emsg) if meridian is None: meridian = DEFAULT_MERIDIAN if antimeridian: meridian += 180 meridian = wrap(meridian)[0] logger.debug( "meridian=%s, antimeridian=%s", meridian, antimeridian, ) slicer = MeridianSlice(mesh, meridian) mesh_whole = slicer.extract(split_cells=False) mesh_split = slicer.extract(split_cells=True) info = mesh.active_scalars_info result: pv.PolyData = mesh.copy(deep=True) meshes = [] remeshed_ids = np.array([]) if mesh_whole.n_cells: ll = to_xy0(mesh_whole) meridian_mask = np.isclose(ll[:, 0], meridian) join_points = np.empty(mesh_whole.n_points, dtype=int) join_points.fill(REMESH_JOIN) mesh_whole[GV_REMESH_POINT_IDS] = join_points mesh_whole[GV_REMESH_POINT_IDS][meridian_mask] = REMESH_SEAM meshes.append(mesh_whole) remeshed_ids = mesh_whole[GV_CELL_IDS] result[GV_REMESH_POINT_IDS] = result[GV_POINT_IDS].copy() if mesh_split.n_cells: remeshed, remeshed_west, remeshed_east = remesh(mesh_split, meridian) meshes.extend([remeshed_west, remeshed_east]) remeshed_ids = np.unique(np.hstack([remeshed_ids, remeshed[GV_CELL_IDS]])) if GV_REMESH_POINT_IDS not in result.point_data: result.point_data[GV_REMESH_POINT_IDS] = result[GV_POINT_IDS].copy() if meshes: result.remove_cells(remeshed_ids, inplace=True) result.set_active_scalars(info.name, preference=info.association.name.lower()) result = combine(result, *meshes) return result