PyVista/Trimesh
Conversion
Pyvista to Trimesh
Convert Pyvista 1-D face array to Trimesh faces
def pyvistaToTrimeshFaces(cells):
faces = []
idx = 0
while idx < len(cells):
curr_cell_count = cells[idx]
curr_faces = cells[idx+1:idx+curr_cell_count+1]
faces.append(curr_faces)
idx += curr_cell_count+1
return np.array(faces)
Reference: AbrarAnwar/cross_section_rl/utils.py
Meshlab to Pyvista
import numpy as np
import pyvista as pv
def meshlab2pv(mmesh):
'''
Convert meshlab mesh to PyVista polydata
'''
mpoints, mcells = mmesh.vertex_matrix(), mmesh.face_matrix()
if len(mcells):
# convert the faces into PolyData format
mfaces = []
for cell in mcells:
face = np.hstack((len(cell), cell))
mfaces.extend(face.tolist())
mfaces = np.array(mfaces)
polydata = pv.PolyData(mpoints, np.hstack(mfaces))
else:
polydata = pv.PolyData(mpoints, None)
return polydata
if __name__ == '__main__':
import pymeshlab as pm
ms = pm.MeshSet()
ms.load_project('data/test.mlp')
print(ms.print_status())
meshes = []
plotter = pv.Plotter()
for i in range(ms.number_meshes()):
mesh_pv = meshlab2pv(ms.mesh(i))
meshes.append(mesh_pv)
plotter.add_mesh(mesh_pv)
plotter.add_mesh_slice_orthogonal(meshes[0])
plotter.show()
Qt GUI
Pyvistaqt Example:
import sys
from PyQt5 import Qt, QtCore
import pyvista as pv
from pyvistaqt import QtInteractor
# from pyvista import themes
# pv.set_plot_theme(themes.DarkTheme())
class MainWidget(Qt.QWidget):
def __init__(self, parent=None, show=True):
super(MainWidget, self).__init__()
self.test_button = Qt.QPushButton("Open")
self.test_button.clicked.connect(self.test_button_event)
self.frame = Qt.QFrame()
self.plotter = QtInteractor(self.frame)
vlayout = Qt.QVBoxLayout()
vlayout.addWidget(self.plotter.interactor)
hlayout = Qt.QHBoxLayout()
hlayout.addWidget(self.test_button)
vlayout.addLayout(hlayout)
self.setLayout(vlayout)
self.setWindowTitle("Test Qt Window")
self.setGeometry(550, 200, 800, 600)
# Enable dragging and dropping onto the GUI
self.setAcceptDrops(True)
self.plotter.show_axes()
self.mesh = None
if show:
self.show()
# The following three methods set up dragging and dropping for the app
def dragEnterEvent(self, e):
if e.mimeData().hasUrls:
e.accept()
else:
e.ignore()
def dragMoveEvent(self, e):
if e.mimeData().hasUrls:
e.accept()
else:
e.ignore()
def dropEvent(self, e):
"""
Drop files directly onto the widget
File locations are stored in fname
:param e:
:return:
"""
if e.mimeData().hasUrls:
e.setDropAction(QtCore.Qt.CopyAction)
e.accept()
# Workaround for OSx dragging and dropping
for url in e.mimeData().urls():
fname = str(url.toLocalFile())
self.fname = fname
self.load_mesh()
else:
e.ignore()
def test_button_event(self):
"""
Open a mesh file
"""
self.fname, _ = Qt.QFileDialog.getOpenFileName(self, 'Open file','',"(*.ply) ;; (*.stl)")
self.load_mesh()
def load_mesh(self):
self.mesh = pv.read(self.fname)
self.plotter.clear()
self.plotter.add_mesh(self.mesh, show_edges=True)
self.plotter.reset_camera()
def save_mesh(self):
"""
Save mesh
"""
self.fname, f_filter = Qt.QFileDialog.getSaveFileName(self, 'Save file', self.fname, "(*.ply) ;; (*.stl)")
pv.save_meshio(self.fname, self.mesh, file_format=f_filter.strip('(*.)'))
# close the window
self.close()
if __name__ == '__main__':
app = Qt.QApplication(sys.argv)
window = MainWidget()
sys.exit(app.exec_())
Optimization visualization with pyvista:
import pyvista as pv
import numpy as np
make_gif = True
# increase n_points for a higher resolution
n_points = 100
xmin, xmax = -1.2, 1.2
bounds = 1.25 * np.array([xmin, xmax, xmin, xmax, 0., 0.])
x = np.linspace(xmin, xmax, n_points)
y = np.linspace(xmin, xmax, n_points)
x, y = np.meshgrid(x, y)
coords = np.array(list(zip(x.flatten(), y.flatten())))
g = x ** 4 + y ** 4
g = g.flatten()
constraint_mask = g <= 1
def func(x, y):
return x ** 3 - y ** 3 + 2
f = func(x, y)
f = f.flatten()
domain_coords = np.zeros((n_points ** 2, 3))
domain_coords[:, :2] = coords
domain = pv.PolyData(domain_coords)
domain = domain.delaunay_2d()
domain_in, _ = domain.remove_points(~constraint_mask)
domain_out, _ = domain.remove_points(constraint_mask)
minimizer_array = np.array([- 0.5 ** 0.25, 0.5 ** 0.25, 0.])
value_array = minimizer_array + np.array([0., 0., func(*minimizer_array[:2])])
minimizer = pv.PolyData(minimizer_array)
dashed = pv.Spline(np.vstack((minimizer_array, value_array)), n_points=20)
cone_direction = np.array([-1., 1., 0.])
cone = pv.Cone(minimizer_array, cone_direction, angle=60, height=20)
cone.points[:, 2] = 0
cone.points *= 1 / (4 * 20) ** 0.5
surface_data = np.zeros((n_points ** 2, 3))
surface_data[:, :2] = coords
surface_data[:, 2] = f
surface = pv.PolyData(surface_data)
surface = pv.PolyData(surface)
surface = surface.delaunay_2d()
surface_in, _ = surface.remove_points(~constraint_mask)
surface_out, _ = surface.remove_points(constraint_mask)
constraint_title = pv.Text3D("Constraint set K", depth=0.2)
constraint_title.points -= constraint_title.points.mean(0)[None, :]
constraint_title.points *= \
1.75 / (constraint_title.points.max() - constraint_title.points.min())
constraint_title.rotate_z(90)
text3d = pv.Text3D("Minimizer x", depth=0.2)
text3d.points -= text3d.points.mean(0)[None, :]
text3d.points /= text3d.points.max() - text3d.points.min()
text3d.rotate_z(90)
text3d.rotate_y(90)
text3d.points += 1.75 * minimizer_array
tangeant = pv.Text3D("Tangeant cone at x", depth=0.2)
tangeant.points -= tangeant.points.mean(0)[None, :]
tangeant.points *= 1.75 / (tangeant.points.max() - tangeant.points.min())
tangeant.rotate_z(90)
tangeant.rotate_x(180)
tangeant.rotate_y(90)
tangeant.points += np.array([-1.5, -1.5, 0])
plotter = pv.Plotter()
plotter.add_mesh(domain_out, color="gray", opacity=0.2)
plotter.add_mesh(domain_in, color="black")
plotter.add_mesh(surface_in, scalars=f[constraint_mask], cmap="hot")
plotter.add_mesh(surface_out, cmap="Greys", opacity=0.2)
plotter.add_mesh(cone, color="blue", opacity=0.3)
plotter.add_mesh(minimizer, color="red", render_points_as_spheres=True,
point_size=15)
plotter.add_mesh(dashed, color="red")
plotter.add_mesh(text3d, color="red")
plotter.add_mesh(tangeant, color="black")
plotter.add_mesh(constraint_title, color="white")
plotter.background_color = "white"
plotter.show_bounds(grid='front', location='outer',
show_zaxis=False, color="black",
bounds=bounds)
if make_gif:
# when the window shows up, close it by pressing the q-Key NOT the quit
# button
plotter.show(auto_close=False)
path = plotter.generate_orbital_path(3., n_points=200,
shift=1.75 * domain_out.length)
plotter.open_movie('orbit.mp4')
plotter.orbit_on_path(path, write_frames=True)
plotter.close()
else:
plotter.show()
Simple QVTKRenderWindowInteractor example in Python:
# coding=utf-8
import sys
from vtkmodules.vtkFiltersSources import vtkConeSource
from vtkmodules.vtkRenderingCore import vtkActor, vtkPolyDataMapper, vtkRenderer
# load implementations for rendering and interaction factory classes
import vtkmodules.vtkRenderingOpenGL2
import vtkmodules.vtkInteractionStyle
import QVTKRenderWindowInteractor as QVTK
QVTKRenderWindowInteractor = QVTK.QVTKRenderWindowInteractor
if QVTK.PyQtImpl == 'PySide6':
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QMainWindow
elif QVTK.PyQtImpl == 'PySide2':
from PySide2.QtCore import Qt
from PySide2.QtWidgets import QApplication, QMainWindow
else:
from PySide.QtCore import Qt
from PySide.QtGui import QApplication, QMainWindow
def QVTKRenderWidgetConeExample(argv):
"""A simple example that uses the QVTKRenderWindowInteractor class."""
# every QT app needs an app
app = QApplication(['QVTKRenderWindowInteractor'])
window = QMainWindow()
# create the widget
widget = QVTKRenderWindowInteractor(window)
window.setCentralWidget(widget)
# if you don't want the 'q' key to exit comment this.
widget.AddObserver("ExitEvent", lambda o, e, a=app: a.quit())
ren = vtkRenderer()
widget.GetRenderWindow().AddRenderer(ren)
cone = vtkConeSource()
cone.SetResolution(8)
coneMapper = vtkPolyDataMapper()
coneMapper.SetInputConnection(cone.GetOutputPort())
coneActor = vtkActor()
coneActor.SetMapper(coneMapper)
ren.AddActor(coneActor)
# show the widget
window.show()
widget.Initialize()
widget.Start()
# start event processing
# Source: https://doc.qt.io/qtforpython/porting_from2.html
# 'exec_' is deprecated and will be removed in the future.
# Use 'exec' instead.
try:
app.exec()
except AttributeError:
app.exec_()
if __name__ == "__main__":
QVTKRenderWidgetConeExample(sys.argv)