"""Generate the pyvista logo. Logos generated with: plot_logo(screenshot='pyvista_logo.png', window_size=(1920, 1080)) plot_logo(screenshot='pyvista_logo_sm.png', window_size=(960, 400), off_screen=True) # different camera angle for square plot cpos = [(-0.3654543687422538, 1.1098808905156292, 9.073223697728247), (2.553950615449191, 0.34145688392081264, 0.06127122762851659), (0.019308531920309947, 0.996708840795678, -0.07873161547192065)] plot_logo(screenshot='pyvista_logo_sm_sq.png', window_size=(960, 960), cpos=cpos, off_screen=True) """ from __future__ import annotations import os from pathlib import Path import numpy as np import pyvista from pyvista import examples from pyvista._deprecate_positional_args import _deprecate_positional_args from pyvista.core import _vtk_core as _vtk from pyvista.core.utilities.features import _voxelize_legacy THIS_PATH = str(Path(os.path.realpath(__file__)).parent) LOGO_TITLE = 'PyVista' def atomize(grid, shift_fac=0.1, scale=0.9): """Break apart and shrink and/or scale the individual cells of a mesh. Parameters ---------- grid : pyvista.UnstructuredGrid The input mesh to atomize. shift_fac : float, default: 0.1 Factor by which to shift the individual cells apart. scale : float, default: 0.9 Factor by which to scale the individual cells. Returns ------- pyvista.UnstructuredGrid The atomized mesh with individually shifted and scaled cells. """ cent = grid.center cells = [] for i in range(grid.n_cells): cell = grid.extract_cells(i) ccent = np.array(cell.center) cell.points[:] = (cell.points - ccent) * scale + ccent cell.points += (ccent - np.array(cent)) * shift_fac cells.append(cell) return cells[0].merge(cells[1:]) def text_3d(string, depth=0.5): """Create 3D text from a given string. Parameters ---------- string : str The string of text to convert into 3D text. depth : float, default: 0.5 The depth of the extrusion used to create the 3D text. Returns ------- pyvista.DataSet The 3D text in the form of a PyVista DataSet. See Also -------- :ref:`create_pixel_art_example` """ from vtkmodules.vtkRenderingFreeType import vtkVectorText # noqa: PLC0415 vec_text = vtkVectorText() vec_text.SetText(string) extrude = _vtk.vtkLinearExtrusionFilter() extrude.SetInputConnection(vec_text.GetOutputPort()) extrude.SetExtrusionTypeToNormalExtrusion() extrude.SetVector(0, 0, 1) extrude.SetScaleFactor(depth) tri_filter = _vtk.vtkTriangleFilter() tri_filter.SetInputConnection(extrude.GetOutputPort()) tri_filter.Update() return pyvista.wrap(tri_filter.GetOutput()) @_deprecate_positional_args def logo_letters(merge=False, depth=0.3): # noqa: FBT002 """Generate a mesh for each letter in "PyVista". Parameters ---------- merge : bool, optional If ``True``, merge the meshes of the individual letters into a single mesh. If ``False``, return a dictionary where the keys are the letters and the values are the respective meshes. depth : float, optional The depth of the extrusion for each letter in the mesh. Returns ------- pyvista.PolyData or dict[str, pyvista.PolyData] If merge is ``True``, returns a single merged mesh containing all the letters in "PyVista". If merge is ``False``, returns a dictionary where the keys are the letters and the values are the respective meshes. """ mesh_letters = pyvista.PolyData() if merge else {} # type: ignore[var-annotated] # spacing between letters space_factor = 0.9 width = 0 for letter in LOGO_TITLE: mesh_letter = text_3d(letter, depth=depth) this_letter_width = mesh_letter.points[:, 0].max() mesh_letter.translate([width * space_factor, 0, 0.0], inplace=True) width += this_letter_width if merge: mesh_letters += mesh_letter else: mesh_letters[letter] = mesh_letter return mesh_letters def logo_voxel(density=0.03): """Create a voxelized PyVista logo. Parameters ---------- density : float, default: 0.03 Density of the voxelization. Returns ------- pyvista.UnstructuredGrid Voxelized PyVista logo as an unstructured grid. """ return _voxelize_legacy(text_3d(LOGO_TITLE, depth=0.3), density=density) def logo_basic(): """Create a basic pyvista logo. Returns ------- pyvista.UnstructuredGrid Grid containing the pyvista letters. Examples -------- Plot the basic pyvista logo. >>> from pyvista import demos >>> logo = demos.logo_basic() >>> cpos = logo.plot(smooth_shading=True) Add scalars and plot the logo. >>> logo['x_coord'] = logo.points[:, 0] >>> cpos = logo.plot( ... scalars='x_coord', ... cmap='Spectral', ... smooth_shading=True, ... cpos='xy', ... ) """ return logo_letters(merge=True).compute_normals(split_vertices=True) @_deprecate_positional_args def plot_logo( # noqa: PLR0917 window_size=None, off_screen=None, screenshot=None, cpos=None, just_return_plotter=False, # noqa: FBT002 show_note=False, # noqa: FBT002 **kwargs, ): """Plot the stylized PyVista logo. Parameters ---------- window_size : sequence[int], optional Size of the window in the format ``[width, height]``. off_screen : bool, optional Renders off screen when ``True``. screenshot : str, optional Save screenshot to path when specified. cpos : list or str, optional Camera position to use. just_return_plotter : bool, default: False Return the plotter instance without rendering. show_note : bool, default: False Show a text in the plot when ``True``. **kwargs : dict, optional Additional keyword arguments. Returns ------- Plotter or camera position Returns the plotter instance if ``just_return_plotter`` is ``True``, otherwise returns the camera position if ``screenshot`` is specified, otherwise shows the plot. Examples -------- >>> from pyvista import demos >>> cpos = demos.plot_logo() """ # initialize plotter if window_size is None: window_size = [960, 400] plotter = pyvista.Plotter(window_size=window_size, off_screen=off_screen) mesh_letters = logo_letters() # letter 'P' p_mesh = mesh_letters['P'].compute_normals(split_vertices=True) plotter.add_mesh(p_mesh, color='#376fa0', smooth_shading=True) # letter 'y' y_mesh = mesh_letters['y'].compute_normals(split_vertices=True) plotter.add_mesh(y_mesh, color='#ffd040', smooth_shading=True) # letter 'V' v_grid = _voxelize_legacy(mesh_letters['V'], density=0.08) v_grid_atom = atomize(v_grid) v_grid_atom['scalars'] = v_grid_atom.points[:, 0] v_grid_atom_surf = v_grid_atom.extract_surface() faces = v_grid_atom_surf.faces.reshape(-1, 5).copy() faces[:, 1:] = faces[:, 1:][:, ::-1] v_grid_atom_surf.faces = faces plotter.add_mesh( v_grid_atom_surf, scalars='scalars', show_edges=True, cmap='winter', show_scalar_bar=False, ) # letter 'i' i_grid = _voxelize_legacy(mesh_letters['i'], density=0.1) plotter.add_mesh( i_grid.extract_surface(), style='points', color='r', render_points_as_spheres=True, point_size=14, ) plotter.add_mesh(i_grid, style='wireframe', color='k', line_width=4) # letter 's' mesh = mesh_letters['s'] mesh['scalars'] = mesh.points[:, 0] plotter.add_mesh( mesh, scalars='scalars', style='wireframe', line_width=2, cmap='gist_heat', backface_culling=True, render_lines_as_tubes=True, show_scalar_bar=False, ) # letter 't' mesh = mesh_letters['t'].clean().compute_normals() scalars = mesh.points[:, 0] plotter.add_mesh(mesh, scalars=scalars, show_edges=True, cmap='autumn', show_scalar_bar=False) # letter 'a' grid = examples.download_letter_a() grid.points[:, 0] += mesh_letters['a'].center[0] - grid.center[0] # select some cells from grid cells = grid.cells.reshape(-1, 5) mask = grid.points[cells[:, 1:], 2] < 0.2 mask = mask.all(1) a_part = grid.extract_cells(mask) cells = a_part.cells.reshape(-1, 5) scalars = grid.points[cells[:, 1], 1] plotter.add_mesh( a_part, scalars=scalars, show_edges=True, cmap='Greens', show_scalar_bar=False ) if show_note: text = text_3d('You can move me!', depth=0.1) text.points *= 0.1 text.translate([4.0, -0.3, 0], inplace=True) plotter.add_mesh(text, color='black') # finalize plot and show it plotter.set_background(kwargs.pop('background', 'white')) plotter.camera_position = 'xy' if 'zoom' in kwargs: plotter.camera.zoom(kwargs.pop('zoom')) # plotter.remove_scalar_bar() plotter.enable_anti_aliasing() if just_return_plotter: return plotter if screenshot: # pragma: no cover plotter.show(cpos=cpos, auto_close=False) plotter.screenshot(screenshot, True) cpos_final = plotter.camera_position plotter.close() return cpos_final else: return plotter.show(cpos=cpos, **kwargs) def logo_atomized(density=0.05, scale=0.6, depth=0.05): """Generate a voxelized pyvista logo with intra-cell spacing. Parameters ---------- density : float, default: 0.05 The spacing between voxels in the generated PyVista logo. scale : float, default: 0.6 The scaling factor for the generated PyVista logo. depth : float, default: 0.05 The depth of the generated PyVista logo. Returns ------- pyvista.UnstructuredGrid A merged UnstructuredGrid representing the voxelized PyVista logo. """ mesh_letters = logo_letters(depth=depth) grids = [] for letter in mesh_letters.values(): grid = _voxelize_legacy(letter, density=density) grids.append(atomize(grid, scale=scale)) return grids[0].merge(grids[1:])