#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import pygimli as pg
from .utils import pgMesh2pvMesh
pv = pg.optImport('pyvista', requiredFor="properly visualize 3D data")
[docs]def drawMesh(ax, mesh, notebook=False, **kwargs):
"""Draw a mesh into a given plotter.
Parameters
----------
ax: pyvista.Plotter [optional]
The plotter to draw everything. If none is given, one will be created.
mesh: pg.Mesh
The mesh to show.
notebook: bool [False]
Sets the plotter up for jupyter notebook/lab.
cMap: str ['viridis']
The colormap string.
bc: pyvista color ['#EEEEEE']
Background color.
style: str['surface']
Possible options:"surface","wireframe","points"
label: str
Data to be plottet. If None the first is taken.
Returns
-------
ax: pyvista.Plotter [optional]
The plotter
"""
# sort out a few kwargs to not confuse the plotter initialization
opacity = kwargs.pop('alpha', kwargs.pop('opacity', 1))
cMap = kwargs.pop('cMap', None)
color = kwargs.pop('color', None)
style = kwargs.pop('style', 'surface')
returnActor = kwargs.pop('returnActor', False)
showMesh = kwargs.pop('showMesh', False)
grid = kwargs.pop('grid', False)
colorBar = kwargs.pop('colorBar', style != 'wireframe')
bc = kwargs.pop('bc', '#EEEEEE') # background color
lw = kwargs.pop('line_width', 0.1)
filt = kwargs.pop('filter', {})
# show_edges = kwargs.pop('show_edges', True) # not used
# name = kwargs.pop('name', 'Mesh') # not used
if isinstance(mesh, pg.Mesh):
mesh = pgMesh2pvMesh(mesh)
dataName = kwargs.pop('label', list(mesh.cell_data.keys())[0])
theme = pv.themes.DefaultTheme()
theme.background = bc
# seems to be broken .. results on pure black screens on some machines
# theme.antialiasing = True
theme.font.color = 'k'
if ax is None:
ax = pv.Plotter(notebook=notebook, theme=theme, **kwargs)
if grid is True:
pass # implementme
ax.show_bounds(all_edges=True, minor_ticks=True)
ax.add_axes()
for k, fi in filt.items():
if k.lower() == 'clip':
if isinstance(mesh, pv.core.pointset.UnstructuredGrid):
fi.setdefault('crinkle', True)
mesh = mesh.clip(**fi)
elif k.lower() == 'threshold':
mesh = mesh.threshold(**fi)
elif k.lower() == 'slice':
mesh = mesh.slice(**fi)
else:
pg.error('filter:', k, 'not yet implemented')
_actor = ax.add_mesh(mesh, # type: pv.UnstructuredGrid
scalars=dataName,
cmap=cMap,
color=color,
style=style,
show_edges=showMesh,
line_width=lw,
# edge_color='white',
show_scalar_bar=colorBar,
opacity=opacity,
)
if returnActor:
return ax, _actor
else:
return ax
[docs]def drawModel(ax=None, mesh=None, data=None, **kwargs):
"""Draw a mesh with given data.
Parameters
----------
ax: pyvista.Plotter [None]
Pyvista's basic Plotter to add the mesh to.
mesh: pg.Mesh
The Mesh to plot.
data: iterable
Data that should be displayed with the mesh.
Returns
-------
ax: pyvista.Plotter [optional]
The plotter
"""
defaultCMap = kwargs.pop('cMap', 'viridis')
dataName = kwargs.pop('label', None)
if all(v is None for v in [ax, mesh, data]):
pg.critical("At least mesh or data should not be None")
return None
if kwargs.pop('markers', False) is True:
# show boundary mesh with markers
data = mesh.boundaryMarkers()
defaultCMap = pg.plt.cm.get_cmap("Set3", max(1, len(pg.unique(data))))
dataName = 'Boundary Marker'
mesh = pgMesh2pvMesh(mesh, data, dataName, boundaries=True)
else:
if data is not None or len(mesh.dataMap()) != 0:
kwargs.setdefault('style', 'surface')
kwargs['color'] = None
if dataName is None and data is not None:
if len(data) == mesh.cellCount():
dataName = 'Cell data'
elif len(data) == mesh.nodeCount():
dataName = 'Node data'
mesh = pgMesh2pvMesh(mesh, data, dataName)
kwargs['cMap'] = defaultCMap
kwargs['label'] = dataName
return drawMesh(ax, mesh, **kwargs)
[docs]def drawSensors(ax, sensors, diam=0.01, color='grey', **kwargs):
"""
Draw the sensor positions to given mesh or the the one in given plotter.
Parameters
----------
ax: pyvista.Plotter
The plotter to draw everything. If none is given, one will be created.
sensors: iterable
Array-like object containing tuple-like (x, y, z) positions.
diam: float [0.01]
Radius of sphere markers.
color: str ['grey']
Color of sphere markers.
Returns
-------
ax: pyvista.Plotter
The plotter containing the mesh and drawn electrode positions.
"""
for pos in sensors:
s = pv.Sphere(radius=diam / 2, center=pos)
ax.add_mesh(s, color=color, **kwargs)
return ax
[docs]def drawSlice(ax, mesh, normal=[1, 0, 0], **kwargs):
"""Draw a slice in a 3D mesh for given pygimli mesh.
Parameters
----------
ax: pyvista.Plotter
The Plotter to draw on.
mesh: pg.Mesh
The mesh to take the slice out of.
normal: list [[1, 0, 0]]
Coordinates to orientate the slice.
Returns
-------
ax: pyvista.Plotter
The plotter containing the mesh and drawn electrode positions.
Keyword arguments passed to pyvista
-----------------------------------
normal: [float, float, float] | str
normal vector constructing the slice
origin: [float, float, float]
origin for the slice (by default mesh center)
generate_triangles: bool [False]
generate triangle mesh
contour: bool [False]
draw contours instead
More information can be found at
https://docs.pyvista.org/api/core/filters.html
"""
label = kwargs.pop('label', None)
data = kwargs.pop('data', None)
mesh = pgMesh2pvMesh(mesh, data, label)
try:
single_slice = mesh.slice(normal, **kwargs)
except AssertionError as e:
# 'contour' kwarg only works with point data and breaks execution
pg.error(e)
else:
# REVIEW: bounds and axes might be confused with the outline..?!
outline = mesh.outline()
ax.add_mesh(outline, color="k")
ax.add_mesh(single_slice)
return ax
[docs]def drawStreamLines(ax, mesh, data, label=None, radius=0.01, **kwargs):
"""
Draw streamlines of given data.
PyVista streamline needs a vector field of gradient data per cell.
Parameters
----------
ax: pyvista.Plotter [None]
The plotter that should be used for visualization.
mesh: pyvista.UnstructuredGrid|pg.Mesh [None]
Structure to plot the streamlines in to.
If pv grid a check is performed if the data set is already contained.
data: iterable [None]
Values used for streamlining.
label: str
Label for the data set. Will be searched for within the data.
radius: float [0.01]
Radius for the streamline tubes.
Note
----
All kwargs will be forwarded to pyvistas streamline filter:
https://docs.pyvista.org/api/core/_autosummary/pyvista.DataSetFilters.streamlines.html
"""
if label is None:
label = 'grad'
if isinstance(mesh, pg.Mesh):
# create gradient of cell data if not provided
if np.ndim(data) == 1:
grad = pg.solver.grad(mesh, data)
else:
grad = data
# ensure that it's point/node data in the mesh
if len(grad) == mesh.cellCount():
grad = pg.meshtools.cellDataToNodeData(mesh, grad)
# add data to the mesh and convert to pyvista grid
mesh = pgMesh2pvMesh(mesh, grad.T, label)
elif isinstance(mesh, pv.UnstructuredGrid):
if label not in mesh.point_arrays: # conversion needed
mesh.cell_data_to_point_data()
if label is None:
label = list(mesh.point_arrays.keys())[0]
# kwargs['vectors'] = label
streams = mesh.streamlines(vectors=label, **kwargs)
ax.add_mesh(streams.tube(radius=radius), show_scalar_bar=False)