How to generate mesh and plot 3d surface in python? - python

I have 6 corner points coordinate of 3d surface like Figure 1. I want to generate and plot 3d surface like Figure 2. I need to find the distance of midpoint of each meshed area from the origin.
Figure 1
Figure 2
Please suggest me, which module will be better for meshing and also for plotting?

PyVista and vtkplotter are very similar and both are built on top of VTK but have pretty different APIs/design choices. For your use, either would be great! Here is the equivalent with PyVista for completeness.
Typically with simple geometries like this, Matplotlib, PyVista, or vtkplotter would all do well. If you start to create more sophisticated meshes and 3D scenes, then that's where PyVista and vtkplotter will excel as they are built for 3D while MPL is really awesome at 2D.
PyVista will especially excel at data management if you start making complex meshes.... shameless plug ;)
import pyvista as pv
import numpy as np
# Define the nodes
pts = np.array([(-5.795555, -4, 1.55291), (-4.829629, -2, 1.294095),
(-5.795555, 1, 1.552914), (-5.536736, -4, 2.51884),
(-4.57081, -2, 2.260021), (-5.536736, 1, 2.51884)])
# Define the quads
faces = np.array([(4,0,3,4,1), (4,1,4,5,2)])
# Instantiate a mesh
mesh = pv.PolyData(pts, faces)
# Create a plotting window and display!
p = pv.Plotter()
# Add the mesh and some labels
p.add_mesh(mesh, show_edges=True)
p.add_point_labels(mesh.points, ["%d"%i for i in range(mesh.n_points)])
# A pretty view position
p.camera_position = [(-11.352247399703748, -3.421477319390501, 9.827830270231935),
(-5.1831825, -1.5, 1.9064675),
(-0.48313206526616853, 0.8593146723923926, -0.16781448484204659)]
# Render it!
p.show()

You can use vedo:
from vedo import *
pts = [(-5.795555, -4, 1.55291), (-4.829629, -2, 1.294095),
(-5.795555, 1, 1.552914), (-5.536736, -4, 2.51884),
(-4.57081, -2, 2.260021), (-5.536736, 1, 2.51884)]
faces = [(0,3,4,1), (1,4,5,2)]
mesh = Mesh([pts, faces]).color('red').alpha(0.3).lineWidth(2)
labels = [Text(i, pos=pts[i], s=.2, c='k') for i in range(len(pts))]
show(mesh, labels)

I don't know what your data looks like, but matplotlib is capable of plotting 3D surfaces based on points with x, y and z coordinates.
See: https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html

Related

Vispy surface plot highlight particular section with color and image

Question 1: I am trying to highlight particular section from Vispy Surface Plot example in a particular colour somewhat similar to below modified image.
Question 2: Similarly I would like to add an image data as overlay texture to the Vispy surface plot
Can SurfacePlotVisual be used for this?
I am unable to find any examples of the SurfacePlotVisual on the internet.
Can anyone please direct me to the efficient way of getting it done using vispy.
Thanks
Update 1:
Adding Sample code for testing
import sys
import numpy as np
from vispy import app, scene, color
from vispy.util.filter import gaussian_filter
from vispy.visuals.filters import TextureFilter
from vispy.io import imread, load_data_file, read_mesh
canvas = scene.SceneCanvas(keys='interactive', bgcolor='w')
view = canvas.central_widget.add_view()
view.camera = scene.TurntableCamera(up='z', fov=60)
# Simple surface plot example
# x, y values are not specified, so assumed to be 0:50
z = np.random.normal(size=(250, 250), scale=200)
z[100, 100] += 50000
print(z.shape)
z = gaussian_filter(z, (10, 10))
p1 = scene.visuals.SurfacePlot(z=z) # , color=(0.3, 0.3, 1, 1))
p1.transform = scene.transforms.MatrixTransform()
p1.transform.scale([1 / 249., 1 / 249., 1 / 249.])
p1.transform.translate([-0.5, -0.5, 0])
#verts = p1._meshdata.get_vertices()
verts = p1._meshdata.get_vertices()[:, :2]
texcoords = (verts - verts.min()) / (verts.max() - verts.min())
texture = imread('spot.png')
texture = np.flip(texture, 0) // flip added to get correct position of the image
print("spot.shape:", texture.shape)
print("textcoords:", texcoords)
texture_filter = TextureFilter(texture, texcoords)
p1.attach(texture_filter)
view.add(p1)
xax = scene.Axis(pos=[[-0.5, -0.5], [0.5, -0.5]], tick_direction=(0, -1),
font_size=16, axis_color='k', tick_color='k', text_color='k',
parent=view.scene)
xax.transform = scene.STTransform(translate=(0, 0, -0.2))
yax = scene.Axis(pos=[[-0.5, -0.5], [-0.5, 0.5]], tick_direction=(-1, 0),
font_size=16, axis_color='k', tick_color='k', text_color='k',
parent=view.scene)
yax.transform = scene.STTransform(translate=(0, 0, -0.2))
# Add a 3D axis to keep us oriented
axis = scene.visuals.XYZAxis(parent=view.scene)
if __name__ == '__main__':
canvas.show()
if sys.flags.interactive == 0:
app.run()
Update 2:
Have updated above code and output image for reference.
Surface plot output is as above...
Would like to remove surface plot shade and instead would like to have same color output for the surface plot output as compared to image....
Answer 1
The set_data method of the SurfacePlotVisual takes an array of colors:
https://github.com/vispy/vispy/blob/df6c6be9c5aa6a67abf7e3072780264886e2be77/vispy/visuals/surface_plot.py#L132-L151
You should be able to pass whatever colors you want to that as an RGBA array (MxNx4). Note these are colors for the vertices apparently (based on the code I'm seeing) so colors will be interpolated between vertices.
Answer 2
The SurfacePlot is a subclass of the MeshVisual which is able to have a TextureFilter applied to it. I've never done it, but theoretically you should be able to follow this example to add a texture to your SurfacePlot:
https://vispy.org/gallery/scene/mesh_texture.html
The key parts are the loading of the image data from spot.png and then creating and attaching the TextureFilter.
Update 1
Here's what I get if I change the texcoords line to:
verts = p1._meshdata.get_vertices()[:, :2]
Kind of creepy. The point is that we're just making an array that maps the individual surface plot verticies (the mesh vertices) to points on the image. These coordinates need to be between 0 and 1. So we're cheating and taking the vertex coordinates of the mesh and normalizing them between 0 and 1. This may not be anything like what you want, but the point is we have texcoords with a shape of (N, 2) where N is the number of mesh vertices and an (x, y) coordinate for each.

Creating a 2D mesh with Meshpy

I want to build a 2D mesh with the Python Module Meshpy. Here is a simple Code for an example:
from meshpy.tet import MeshInfo, build
mesh_info = MeshInfo()
mesh_info.set_points([
(0,0,0), (2,0,0), (2,2,0), (0,2,0),
])
mesh_info.set_facets([
[0,1,2,3],
])
mesh = build(mesh_info)
How you can see its just a simple square in 2D. If i want to try to mesh it, Python crashes with the error Code "Exception: All vertices are coplanar (Tol = 1e-08)".
Is there a possibility for creating meshes with Meshpy in 2D or does it only works in 3D?
And if its possible, can i change the size of the mesh?

python, vtk glyphs with independent position, orientation, color, and height

Using Python and VTK I am trying to render 10k cylinders to visualize gradient directions. I have reviewed multiple examples on the internet but none of them shows how you can, at the same time, change the position, orientation, color and height of each rendered cylinder independently.
I know it is possible to create an actor per cylinder but with 10k cylinders that would be very slow.
The following example code is my work in progress on this problem. I first define some data for 2 cylinders and next I try to visualize them with VTK.
What works is the position, orientation and color of the cylinders.
What does not work is the height of each cylinder.
Possible solutions might be to use different cylinder sources to get the heights right. Yet I don't know how to apply those.
Perhaps a more experienced VTK programmer can enlighten me?
from vtk import *
# input data, every row is for a different item
positions = [[0, 0, 0],
[1.5, 0, 0]]
orientations = [[1.0, 0.0, 0.0],
[0.0, 1.0, 1.0]]
colors = [[255, 0, 0],
[0, 255, 255]]
heights = [1,
2]
# rendering of those two defined cylinders
points = vtkPoints()
points.InsertNextPoint(*positions[0])
points.InsertNextPoint(*positions[1])
polydata = vtkPolyData()
polydata.SetPoints(points)
color_def = vtkUnsignedCharArray()
color_def.SetNumberOfComponents(3)
color_def.SetNumberOfTuples(polydata.GetNumberOfPoints())
color_def.InsertTuple3(0, *colors[0])
color_def.InsertTuple3(1, *colors[1])
polydata.GetPointData().SetScalars(color_def)
pointNormalsArray = vtkDoubleArray()
pointNormalsArray.SetNumberOfComponents(3)
pointNormalsArray.SetNumberOfTuples(polydata.GetNumberOfPoints())
pointNormalsArray.SetTuple(0, orientations[0])
pointNormalsArray.SetTuple(1, orientations[1])
polydata.GetPointData().SetNormals(pointNormalsArray)
cyl_source = vtkCylinderSource()
cyl_source.SetResolution(10)
cyl_source.SetHeight(0.8)
cyl_source.SetRadius(0.1)
cyl_source.Update()
glyph = vtkGlyph3D()
glyph.SetInputData(polydata)
glyph.SetSourceConnection(cyl_source.GetOutputPort())
glyph.SetColorModeToColorByScalar()
glyph.SetVectorModeToUseNormal()
glyph.ScalingOff()
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(glyph.GetOutputPort())
actor = vtkActor()
actor.SetMapper(mapper)
ren = vtkRenderer()
ren.AddActor(actor)
renwin = vtk.vtkRenderWindow()
renwin.AddRenderer(ren)
iren = vtk.vtkRenderWindowInteractor()
iren.SetRenderWindow(renwin)
renwin.Render()
iren.Initialize()
iren.Start()
It seems that http://www.vtk.org/Wiki/VTK/Examples/Python/Visualization/ClampGlyphSizes has something similar to what you need (I haven't tried):
# Tell glyph which attribute arrays to use for what
glyph.SetInputArrayToProcess(0,0,0,0,'Elevation') # scalars
glyph.SetInputArrayToProcess(1,0,0,0,'RTDataGradient') # vectors
# glyph.SetInputArrayToProcess(2,0,0,0,'nothing') # normals
glyph.SetInputArrayToProcess(3,0,0,0,'RTData') # colors
From the documentation http://www.vtk.org/doc/nightly/html/classvtkGlyph3D.html#details :
You can set what arrays to use for the scalars, vectors, normals, and
color scalars by using the SetInputArrayToProcess methods in
vtkAlgorithm. The first array is scalars, the next vectors, the next
normals and finally color scalars.
vtkGlyphMapper and vtkGlyph3DMapper allow you to set a 3-dimensional ScaleArray, which you can then add to the point data:
glyph.SetScaleArray("CylinderScales")
scaleArray = vtkDoubleArray()
scaleArray.SetName("CylinderScales")
scaleArray.SetNumberOfComponents(3)
scaleArray.SetNumberOfTuples(polydata.GetNumberOfPoints())
scaleArray.SetTuple(0, (1, 1, 1))
scaleArray.SetTuple(1, (1, 1, 2))
polydata.GetPointData().AddArray(scaleArray)
The second cylinder should be twice as tall as the first, unless I got my axes wrong.

matplotlib quiver arrow direction

I am trying to plot grid of vectors. However when I load my files, vectors actually point to the 45 degree wrong direction, but following the patter from my data. In quiver howto, it is said it points 45 degrees when the vectors are the same, can this be changed?
Also when I tried to use some random number the quiver function acted quite randomly. (using numbers or generating angle grid by arctan(y/x)*180/3.1415). My grid of vectors should look like its rotating - vortex around the centre, instead it looks like antivortex blowing out of the centre.
from pylab import *
from numpy import ma
import scipy.io as c
import math
X,Y = meshgrid( arange(0,100,1),arange(0,100,1) )
ufile = np.genfromtxt(r'x.txt')
vfile = np.genfromtxt(r'y.txt')
U = ufile
V = vfile
angle = (((abs(U)/U+1)/2)*((abs(V)/V+1)/2)*arctan(V/U)+((abs(V)/V+1)/2)*((abs(U)/U-1)/2)‌​*(-arctan(V/U)+math.pi)+((abs(V)/V-1)/2)*((abs(U)/U-1)/2)*(arctan(V/U)+math.pi)+(‌​(abs(U)/U+1)/2)*((abs(V)/V-1)/2)*(-arctan(V/U)+2*math.pi))*180/math.pi+90
scale = 10
figure()
Q = quiver( X[::scale, ::scale], Y[::scale, ::scale], U[::scale, ::scale], V[::scale, ::scale],
pivot='mid', color='k', units='xy', headaxislength=20, angles=angle[::scale, ::scale] )
axis([0, 100, 0, 100])
show()

annotating many points with text in mayavi using mlab

I'm trying to annotate points plotted with the points3d() function using mayavi.mlab.
Each point is associated with a label which I would like to plot next to the points using the text3d() function. Plotting the points is fast, however the mlab.text3d() function does not seem to accept arrays of coordinates, so I have to loop over the points and plot the text individually, which is very slow:
for i in xrange(0, self.n_labels):
self.mlab_data.append(
mlab.points3d( pX[self.labels == self.u_labels[i], 0],
pX[self.labels == self.u_labels[i], 1],
pX[self.labels == self.u_labels[i], 2],
color=self.colours[i],
opacity=1,
scale_mode="none",
scale_factor=sf ) )
idcs, = np.where(self.labels == self.u_labels[i])
for n in idcs.flatten():
mlab.text3d( pX[n, 0],
pX[n, 1],
pX[n, 2],
"%d" % self.u_labels[i],
color=self.colours[i],
opacity=1,
scale=sf )
Any ideas how I could speed this up? Also, is it possible to add a legend (as for instance in matplotlib), I couldn't find anything in the docs.
Thanks,
Patrick
The way you are doing it above will render the scene every time you plot a point or text. This is slow. You can disable the scene rendering, do the plotting and then render the scene by figure.scene.disable_render = True/False:
import scipy
from mayavi import mlab
X = 100 * scipy.rand(100, 3)
figure = mlab.figure('myfig')
figure.scene.disable_render = True # Super duper trick
mlab.points3d(X[:,0], X[:,1], X[:,2], scale_factor=0.4)
for i, x in enumerate(X):
mlab.text3d(x[0], x[1], x[2], str(i), scale=(2, 2, 2))
figure.scene.disable_render = False # Super duper trick
I use this trick and others in Figure class in morphic Viewer https://github.com/duanemalcolm/morphic/blob/master/morphic/viewer.py
Another good trick in the code is to reuse existing objects, i.e., if you've plotted the text already, don't replot them, just update their position and text attributes. This means saving the mlab object. You can see how I do this in morphic.Viewer.

Categories

Resources