diff --git a/README.md b/README.md
index 3849d6c1..e24fa2ca 100644
--- a/README.md
+++ b/README.md
@@ -91,12 +91,13 @@ git clone https://github.com/marcomusy/vtkplotter.git
cd vtkplotter/examples
python tutorial.py
```
-**More than 100 examples can be found in directories** _(scroll down to see the screenshots):_
+**More than 150 examples can be found in directories** _(scroll down to see the screenshots):_
[**examples/basic**](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic)
[**examples/advanced**](https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced)
[**examples/volumetric**](https://github.com/marcomusy/vtkplotter/blob/master/examples/volumetric)
[**examples/simulations**](https://github.com/marcomusy/vtkplotter/blob/master/examples/simulations)
-[**examples/other**](https://github.com/marcomusy/vtkplotter/blob/master/examples/other).
+[**examples/other**](https://github.com/marcomusy/vtkplotter/blob/master/examples/other)
+[**examples/other/dolfin**](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin).
| | |
|:-----------------------------------------------------------------------------------------------------------------:|:-----|
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 66789e6b..223220ad 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -35,8 +35,15 @@
copyright = '2019, M. Musy'
author = 'Marco Musy'
-# The short X.Y version
-version = '2019.1.4'
+# package version
+try:
+ VERSIONFILE = "../../vtkplotter/version.py"
+ verstrline = open(VERSIONFILE, "rt").read()
+ verstr = verstrline.split('=')[1].replace('\n','').replace("'","")
+except:
+ verstr='unknown_version'
+
+version = verstr
# -- General configuration ---------------------------------------------------
diff --git a/examples/README.md b/examples/README.md
index d18f82e5..385eae00 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -21,9 +21,15 @@ vp = Plotter(title='first example')
# (The actual mesh corresponds to the outer shape of
# an embryonic mouse limb at about 11 days of gestation).
# Choose a tomato color for the internal surface of the mesh, and no transparency.
-vp.load('data/270.vtk', c='b', bc='tomato') # c=(R,G,B), letter or color name, b=blue
+vp.load(datadir+'270.vtk', c='b', bc='tomato') # c=(R,G,B), letter or color name, b=blue
vp.show() # picks what was stored in python list vp.actors
# Press Esc to close the window and exit python session, or q to continue
+
+# The same result can be achieved in an even simpler way:
+from vtkplotter import *
+mesh = load(datadir+'270.vtk', c='b', bc='tomato')
+# a Plotter instance is automatically generated
+mesh.show()
```
![tut1](https://user-images.githubusercontent.com/32848391/50738980-d9227400-11d9-11e9-8a7c-14b2abc4d41f.jpg)
@@ -33,9 +39,9 @@ vp.show() # picks what was stored in python list vp.actors
# by default use their file names as legend entries.
# No need to use any variables, as actors are stored internally in vp.actors:
vp = Plotter(title='3 shapes')
-vp.load('data/250.vtk', c=(1,0.4,0), alpha=0.3) # set opacity to 30%
-vp.load('data/270.vtk', c=(1,0.6,0), alpha=0.3)
-vp.load('data/290.vtk', c=(1,0.8,0), alpha=0.3)
+vp.load(datadir+'250.vtk', c=(1,0.4,0), alpha=0.3) # set opacity to 30%
+vp.load(datadir+'270.vtk', c=(1,0.6,0), alpha=0.3)
+vp.load(datadir+'290.vtk', c=(1,0.8,0), alpha=0.3)
print('Loaded vtkActors: ', len(vp.actors))
vp.show()
```
@@ -88,15 +94,15 @@ vp.show(Cylinder(), at=8, legend='cylinder', interactive=1)
# Draw a bunch of objects from various mesh formats. Loading is automatic.
vp = Plotter(shape=(3,3), title='mesh formats') # split window in 3 rows and 3 columns
vp.sharecam = False # each object can be moved independently
-vp.show('data/beethoven.ply', at=0, c=0, axes=0) # dont show axes
-vp.show('data/cow.byu', at=1, c=1, zoom=1.15) # make it 15% bigger
-vp.show('data/limb.pcd', at=2, c=2)
-vp.show('data/ring.gmsh', at=3, c=3, wire=1) # show mesh as wireframe
-vp.show('data/images/dog.jpg',at=4) # 2d images can be loaded the same way
-vp.show('data/shuttle.obj', at=5, c=5)
-vp.show('data/shapes/man.vtk',at=6, c=6, axes=2) # show negative axes segments
-vp.show('data/teapot.xyz', at=7, c=7, axes=3) # hide negative axes
-vp.show('data/pulley.vtu', at=8, c=8, interactive=1) # try to click object and press k
+vp.show(datadir+'beethoven.ply', at=0, c=0, axes=0) # dont show axes
+vp.show(datadir+'cow.byu', at=1, c=1, zoom=1.15) # make it 15% bigger
+vp.show(datadir+'limb.pcd', at=2, c=2)
+vp.show(datadir+'ring.gmsh', at=3, c=3, wire=1) # show mesh as wireframe
+vp.show(datadir+'images/dog.jpg',at=4) # 2d images can be loaded the same way
+vp.show(datadir+'shuttle.obj', at=5, c=5)
+vp.show(datadir+'shapes/man.vtk',at=6, c=6, axes=2) # show negative axes segments
+vp.show(datadir+'teapot.xyz', at=7, c=7, axes=3) # hide negative axes
+vp.show(datadir+'pulley.vtu', at=8, c=8, interactive=1) # try to click object and press k
```
![tut7](https://user-images.githubusercontent.com/32848391/50738975-d889dd80-11d9-11e9-97a1-647a9a044718.jpg)
@@ -105,7 +111,7 @@ vp.show('data/pulley.vtu', at=8, c=8, interactive=1) # try to click object an
# Increase the number of vertices of a mesh using subdivide().
# Show the mesh before and after in two separate renderers defined by shape=(1,2)
vp = Plotter(shape=(1,2), axes=0) # dont show axes
-a1 = vp.load('data/beethoven.ply')
+a1 = vp.load(datadir+'beethoven.ply')
coords1 = a1.coordinates() # get coordinates of mesh vertices
pts1 = vp.points(coords1, r=4, c='g', legend='#points = '+str(len(coords1)))
@@ -125,7 +131,7 @@ vp.show([a2, pts2], at=1, interactive=True)
# point at x=500 and has normal (0, 0.3, -1).
# Wildcards can be used to load multiple files or entire directories:
vp = Plotter(title='Cut a surface with a plane')
-vp.load('data/2*0.vtk', c='orange', bc='aqua')
+vp.load(datadir+'2*0.vtk', c='orange', bc='aqua')
for a in vp.actors:
a.cutWithPlane(origin=(500,0,0), normal=(0,0.3,-1), showcut=True)
vp.show()
@@ -143,7 +149,7 @@ vp.renderer # holds the current vtkRenderer
vp.renderers # holds the list of renderers
vp.interactor # holds the vtkWindowInteractor object
vp.interactive # (True) allows to interact with renderer after show()
-vp.camera # holds the current vtkCamera
+vp.camera # holds the current vtkCamera object
vp.sharecam # (True) share the same camera in multiple renderers
```
@@ -159,10 +165,9 @@ actor.rotate(angle, axis) # rotate actor around axis
actor.color(name) # sets/gets color
actor.alpha(value) # sets/gets opacity
actor.N() # get number of vertex points defining the actor's mesh
-actor.polydata() # get the actor's mesh polydata in its current transformation
+actor.polydata() # get the actor's mesh vtkPolyData in its current transformation
actor.coordinates() # get a copy of vertex points coordinates (copy=False to get references)
-actor.normals() # get the list of normals at the vertices of the surface
-actor.clone() # get a copy of actor
+actor.clone() # generate a copy of actor
...
```
diff --git a/examples/advanced/geodesic.py b/examples/advanced/geodesic.py
index db9142ef..387300e5 100644
--- a/examples/advanced/geodesic.py
+++ b/examples/advanced/geodesic.py
@@ -15,4 +15,4 @@
doc = Text(__doc__, c="w")
-show(s, Earth(lw=1), doc, paths, viewup="z", verbose=0)
+show(s, Earth(lw=1), doc, paths, viewup="z")
diff --git a/examples/advanced/mesh_smoothers.py b/examples/advanced/mesh_smoothers.py
index 0023269b..d2b00b8d 100644
--- a/examples/advanced/mesh_smoothers.py
+++ b/examples/advanced/mesh_smoothers.py
@@ -2,7 +2,7 @@
Mesh smoothing with two different VTK methods.
See also analogous Plotter method smoothMLS2D()
-in exammples/advanced/moving_least_squares2D.py
+in exammples/advanced/moving_least_squares2D.py
"""
print(__doc__)
from vtkplotter import Plotter, datadir
diff --git a/examples/advanced/moving_least_squares1D.py b/examples/advanced/moving_least_squares1D.py
index 6233d60c..ea249531 100644
--- a/examples/advanced/moving_least_squares1D.py
+++ b/examples/advanced/moving_least_squares1D.py
@@ -1,11 +1,11 @@
"""
This example shows how to use a variant of a 1 dimensional
-Moving Least Squares (MLS) algorithm to project a cloud
+Moving Least Squares (MLS) algorithm to project a cloud
of unordered points to become a smooth line.
The parameter f controls the size of the local regression.
The input actor's polydata is modified by the method
so more than one pass is possible.
-If showNLines>0 an actor is built demonstrating the
+If showNLines>0 an actor is built demonstrating the
details of the regression for some random points
"""
from __future__ import division, print_function
diff --git a/examples/advanced/recosurface.py b/examples/advanced/recosurface.py
index 51bec40a..b55ff63f 100644
--- a/examples/advanced/recosurface.py
+++ b/examples/advanced/recosurface.py
@@ -41,5 +41,5 @@
vp.show(act_pts1, at=2)
# reconstructed surface from point cloud
-act_reco = recoSurface(act_pts1, bins=128).legend("surf reco")
+act_reco = recoSurface(act_pts1, bins=128).legend("surf reco")
vp.show(act_reco, at=3, axes=7, interactive=1)
diff --git a/examples/advanced/splitmesh.py b/examples/advanced/splitmesh.py
index bfaef07b..5e88e575 100644
--- a/examples/advanced/splitmesh.py
+++ b/examples/advanced/splitmesh.py
@@ -1,5 +1,5 @@
"""
-Split a mesh by connectivity and order the pieces
+Split a mesh by connectivity and order the pieces
by increasing area.
"""
print(__doc__)
diff --git a/examples/advanced/thinplate_grid.py b/examples/advanced/thinplate_grid.py
index f6fd8ad2..6e10d514 100644
--- a/examples/advanced/thinplate_grid.py
+++ b/examples/advanced/thinplate_grid.py
@@ -32,4 +32,4 @@
apts = Points(ptsource, r=5, c="r")
arrs = Arrows(ptsource, pttarget)
-show(warped, apts, arrs, Text(__doc__), axes=9, viewup="z", verbose=0, bg="w")
+show(warped, apts, arrs, Text(__doc__), axes=9, viewup="z", bg="w")
diff --git a/examples/basic/README.md b/examples/basic/README.md
index a2128a94..87d5a929 100644
--- a/examples/basic/README.md
+++ b/examples/basic/README.md
@@ -57,6 +57,8 @@ python example.py
| | |
| [![mesh_custom](https://user-images.githubusercontent.com/32848391/51390972-20d9c180-1b31-11e9-955d-025f1ef24cb7.png)](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/mesh_custom.py)
`mesh_custom.py` | Build a custom color map to specify the color for each vertex of a mesh. |
| | |
+| [![mesh_map2cell](https://user-images.githubusercontent.com/32848391/56600859-0153a880-65fa-11e9-88be-34fd96b18e9a.png)](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/mesh_map2cell.py)
`mesh_map2cell.py` | Map a scalar which is defined on the vertices to the mesh cells. |
+| | |
| [![mesh_threshold](https://user-images.githubusercontent.com/32848391/51807663-4762cf80-228a-11e9-9d0c-184bb11a97bf.png)](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/mesh_threshold.py)
`mesh_threshold.py` | Extracts the cells where scalar value satisfies a threshold criterion. |
| | |
| [![mirror](https://user-images.githubusercontent.com/32848391/50738855-bf346180-11d8-11e9-97a0-c9aaae6ce052.jpg)](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/mirror.py)
`mirror.py` | Mirror-reflect a mesh with respect to one of the cartesian axes. |
diff --git a/examples/basic/buildpolydata.py b/examples/basic/buildpolydata.py
index f4b29619..0e76439a 100644
--- a/examples/basic/buildpolydata.py
+++ b/examples/basic/buildpolydata.py
@@ -1,6 +1,6 @@
"""
-Load a mesh, extract the vertex coordinates,
-and build a new vtkPolyData object.
+Load a mesh, extract the vertex coordinates,
+and build a new vtkPolyData object.
Faces (vertex connectivity) can be specified too.
(press p to increase point size)
diff --git a/examples/basic/buttons.py b/examples/basic/buttons.py
index e872ce11..1c5113bc 100644
--- a/examples/basic/buttons.py
+++ b/examples/basic/buttons.py
@@ -1,5 +1,5 @@
"""
-Add a square button with N possible internal states
+Add a square button with N possible internal states
to a rendering window that calls an external function.
Available fonts: arial, courier, times
"""
diff --git a/examples/basic/connCells.py b/examples/basic/connCells.py
index 5f3aa9ee..89e53e9f 100644
--- a/examples/basic/connCells.py
+++ b/examples/basic/connCells.py
@@ -32,6 +32,6 @@
show(pactor, d, piece, Point(p, c='r'), interactive=0)
tomerge.append(piece)
-
+
show(mergeActors(tomerge).clean(), interactive=1)
diff --git a/examples/basic/distance2mesh.py b/examples/basic/distance2mesh.py
index 90c46b8b..b4bbadc3 100644
--- a/examples/basic/distance2mesh.py
+++ b/examples/basic/distance2mesh.py
@@ -1,5 +1,5 @@
"""
-Computes the (signed) distance
+Computes the (signed) distance
from one mesh to another.
"""
from vtkplotter import Sphere, Cube, show, Text
@@ -13,4 +13,4 @@
#print(s1.scalars("Distance"))
-show(s1, s2, Text(__doc__))
\ No newline at end of file
+show(s1, s2, Text(__doc__))
diff --git a/examples/basic/flatarrow.py b/examples/basic/flatarrow.py
index 2bae0833..105b3d8e 100644
--- a/examples/basic/flatarrow.py
+++ b/examples/basic/flatarrow.py
@@ -9,5 +9,5 @@
l2 = [[sin(x)+c+0.1, -cos(x)+s + x/15, x] for x in arange(0,3, 0.1)]
FlatArrow(l1, l2, c=i, tipSize=1, tipWidth=1)
-
+
show(collection(), viewup="z", axes=1, bg="w")
diff --git a/examples/basic/glyphs.py b/examples/basic/glyphs.py
index 9e8e891b..36e10890 100644
--- a/examples/basic/glyphs.py
+++ b/examples/basic/glyphs.py
@@ -23,7 +23,7 @@
scaleByVectorSize=True,
)
-show(s, gsphere1, t, at=0, N=2, verbose=0)
+show(s, gsphere1, t, at=0, N=2)
#######################################
diff --git a/examples/basic/glyphs_arrows.py b/examples/basic/glyphs_arrows.py
new file mode 100644
index 00000000..b215f830
--- /dev/null
+++ b/examples/basic/glyphs_arrows.py
@@ -0,0 +1,29 @@
+"""
+Draw color arrows.
+"""
+from vtkplotter import *
+import numpy as np
+
+s1 = Sphere(r=10, res=8).c('white').wire()
+s2 = Sphere(r=20, res=8).c('white').wire().alpha(0.1).pos(0,4,0)
+
+coords1 = s1.coordinates() # get the vertices coords
+coords2 = s2.coordinates()
+
+# color can be a colormap which maps arrrow sizes
+a1 = Arrows(coords1, coords2, c='coolwarm', alpha=0.4)
+a1.addScalarBar()
+
+
+# get a list of random rgb colors
+nrs = np.random.randint(0, 10, len(coords1))
+cols = getColor(nrs)
+
+a2 = Arrows(coords1, coords2, c=cols, scale=0.5)
+
+t1 = Text('color arrows by size\nusing a color map')
+t2 = Text('color arrows by an array\nand scale them')
+
+# draw 2 group of actors on two renderers
+show([[s1, s2, a1, t1], [s1, s2, a2, t2]], N=2)
+
diff --git a/examples/basic/keypress.py b/examples/basic/keypress.py
index 783a5571..b5350013 100644
--- a/examples/basic/keypress.py
+++ b/examples/basic/keypress.py
@@ -1,7 +1,7 @@
"""
This example shows how to implement a custom function that is triggered by
pressing a keyboard button when the rendering window is in interactive mode.
- Every time a key is pressed the picked point of the mesh is used
+ Every time a key is pressed the picked point of the mesh is used
to add a sphere and some info is printed.
"""
from vtkplotter import Plotter, printc, Sphere, Text, datadir
diff --git a/examples/basic/largestregion.py b/examples/basic/largestregion.py
index a4357cdb..a93af9d2 100644
--- a/examples/basic/largestregion.py
+++ b/examples/basic/largestregion.py
@@ -1,5 +1,5 @@
"""
-Extract the mesh region that
+Extract the mesh region that
has the largest connected surface
"""
from vtkplotter import *
diff --git a/examples/basic/latex.py b/examples/basic/latex.py
index ed241c6a..251f2e79 100644
--- a/examples/basic/latex.py
+++ b/examples/basic/latex.py
@@ -1,18 +1,20 @@
-from vtkplotter import Latex, Cube, Point, show
+from vtkplotter import Latex, Point, show
# https://matplotlib.org/tutorials/text/mathtext.html
latex1 = r'x= \frac{ - b \pm \sqrt {b^2 - 4ac} }{2a}'
-latex2 = r'$\mathcal{A}\mathrm{sin}(2 \omega t)$'
+latex2 = r'\mathcal{A}\mathrm{sin}(2 \omega t)'
latex3 = r'I(Y | X)=\sum_{x \in \mathcal{X}, y \in \mathcal{Y}} p(x, y) \log \left(\frac{p(x)}{p(x, y)}\right)'
latex4 = r'\Gamma_{\epsilon}(x)=\left[1-e^{-2 \pi \epsilon}\right]^{1-x} \prod_{n=0}^{\infty} \frac{1-\exp (-2 \pi \epsilon(n+1))}{1-\exp (-2 \pi \epsilon(x+n))}'
latex5 = r'\left( \begin{array}{l}{c t^{\prime}} \\ {x^{\prime}} \\ {y^{\prime}} \\ {z^{\prime}}\end{array}\right)=\left( \begin{array}{cccc}{\gamma} & {-\gamma \beta} & {0} & {0} \\ {-\gamma \beta} & {\gamma} & {0} & {0} \\ {0} & {0} & {1} & {0} \\ {0} & {0} & {0} & {1}\end{array}\right) \left( \begin{array}{l}{c t} \\ {x} \\ {y} \\ {z}\end{array}\right)'
latex6 = r'\mathrm{CO}_{2}+6 \mathrm{H}_{2} \mathrm{O} \rightarrow \mathrm{C}_{6} \mathrm{H}_{12} \mathrm{O}_{6}+6 \mathrm{O}_{2}'
-latex7 = r'x~\mathrm{(arb. units)}'
+latex7 = r'x \mathrm{(arb. units)}'
-p = Point()
-c = Cube().wire()
+l = Latex(latex4, s=1, c='white', bg='', alpha=0.9, usetex=False, fromweb=False)
+l.crop(0.3, 0.3) # crop top and bottom 30%
+l.pos(2,0,0)
-l = Latex(latex5, s=1, c='white', bg='', alpha=0.9, fromweb=False).addPos(4,0,-1)
+p = Point()
+box = l.box() # return the bounding box of an actor
-show(p, c, l, axes=8)
\ No newline at end of file
+show(p, l, box, axes=8)
diff --git a/examples/basic/markpoint.py b/examples/basic/markpoint.py
index ef9d3b06..824f83d2 100644
--- a/examples/basic/markpoint.py
+++ b/examples/basic/markpoint.py
@@ -1,5 +1,6 @@
"""
-Mark a specific point on a mesh with some text.
+Mark a specific point
+on a mesh with some text.
"""
from vtkplotter import Sphere, Point, show, Text
diff --git a/examples/basic/mesh_bands.py b/examples/basic/mesh_bands.py
index 6f1a5686..e3e6d25b 100644
--- a/examples/basic/mesh_bands.py
+++ b/examples/basic/mesh_bands.py
@@ -6,7 +6,7 @@
from vtkplotter import show, Hyperboloid, Torus, Text
from numpy import linspace
-doc = Text(__doc__, c="w", bg="lg")
+doc = Text(__doc__, c="k", bg="lg")
hyp = Hyperboloid()
@@ -18,4 +18,4 @@
transp = linspace(1, 0.5, len(scalars)) # set transparencies from 1 -> .5
tor.pointColors(scalars, alpha=transp, bands=3, cmap="winter")
-show(hyp, tor, doc, viewup="z", depthpeeling=1, axes=2, verbose=0)
+show(hyp, tor, doc, viewup="z", depthpeeling=1, axes=2)
diff --git a/examples/basic/mesh_coloring.py b/examples/basic/mesh_coloring.py
index c27ce09a..2ed4ca58 100644
--- a/examples/basic/mesh_coloring.py
+++ b/examples/basic/mesh_coloring.py
@@ -5,7 +5,7 @@
"""
print(__doc__)
-from vtkplotter import Plotter, Text, datadir
+from vtkplotter import *
import numpy as np
vp = Plotter(N=3)
diff --git a/examples/basic/mesh_custom.py b/examples/basic/mesh_custom.py
index 96c5a818..8b81b727 100644
--- a/examples/basic/mesh_custom.py
+++ b/examples/basic/mesh_custom.py
@@ -3,7 +3,7 @@
individual cell or point of an actor's mesh.
Keyword depthpeeling may improve the rendering of transparent objects.
"""
-from vtkplotter import load, Text, show, datadir
+from vtkplotter import *
doc = Text(__doc__, pos=1, c="w")
@@ -13,16 +13,19 @@
# let the scalar be the z coordinate of the mesh vertices
scals = man.coordinates()[:, 2]
-# custom color map with optional opacity (here: 1, 0.2 and 0.8)
-mymap = ["darkblue", "cyan 0.2", (1, 0, 0, 0.8)]
+# custom color map with specified opacities
+#mymap = ["darkblue", "cyan", (1, 0, 0)]
+#alphas = [0.8, 0.4, 0.2]
-# - or by predefined color numbers:
-# mymap = [i for i in range(10)]
+# - OR by predefined color numbers:
+mymap = [i for i in range(10)]
+alphas = [i/10. for i in range(10)]
-# - or by generating a palette betwwen 2 colors:
+# - OR by generating a palette betwwen 2 colors:
# from vtkplotter.colors import makePalette
-# mymap = makePalette('pink', 'green', N=500, hsv=True)
+#mymap = makePalette('pink', 'green', N=500, hsv=True)
+#alphas = 1
-man.pointColors(scals, cmap=mymap).addScalarBar()
+man.pointColors(scals, cmap=mymap, alpha=alphas).addScalarBar()
show(man, doc, viewup="z", axes=8, depthpeeling=1)
diff --git a/examples/basic/mesh_map2cell.py b/examples/basic/mesh_map2cell.py
new file mode 100644
index 00000000..820c93ce
--- /dev/null
+++ b/examples/basic/mesh_map2cell.py
@@ -0,0 +1,24 @@
+"""How to transform/map an array
+which is defined on the vertices of a mesh
+to its cells with mapPointsToCells()
+"""
+from vtkplotter import *
+
+mesh1 = load(datadir+'shapes/icosahedron.vtk')
+
+# let the scalar be the z coordinate of the mesh vertices
+scals = mesh1.coordinates()[:, 2]
+
+mesh1.lineWidth(0.1).addPointScalars(scals, name='scals')
+mesh2 = mesh1.clone().addScalarBar().mapPointsToCells()
+
+doc = Text(__doc__, pos=8, c="w")
+msg1 = Text("Scalar originally defined on points..", pos=5, c="w")
+msg2 = Text("..is interpolated to cells.", pos=5, c="w")
+
+printInfo(mesh1)
+printInfo(mesh2)
+
+show(mesh1, msg1, doc, at=0, N=2, axes=1, viewup="z")
+show(mesh2, msg2, at=1, interactive=True)
+
diff --git a/examples/basic/mesh_threshold.py b/examples/basic/mesh_threshold.py
index 81dd50f5..b5ee6474 100644
--- a/examples/basic/mesh_threshold.py
+++ b/examples/basic/mesh_threshold.py
@@ -1,8 +1,8 @@
"""
-Extracts the cells where scalar value
+Extracts the cells where scalar value
satisfies a threshold criterion.
"""
-from vtkplotter import load, Text, show, datadir
+from vtkplotter import *
doc = Text(__doc__)
@@ -16,5 +16,7 @@
# make a copy and threshold the mesh
cutman = man.clone().threshold(scals, vmin=36.9, vmax=37.5)
+printInfo(cutman)
+
# distribute the actors on 2 renderers
show([[man, doc], cutman], N=2, elevation=-30, axes=0)
diff --git a/examples/basic/multiwindows.py b/examples/basic/multiwindows.py
index bb0d3deb..aa0189d5 100644
--- a/examples/basic/multiwindows.py
+++ b/examples/basic/multiwindows.py
@@ -1,7 +1,7 @@
"""
Example of drawing objects on different windows
and/or subwindows within the same window.
-We split the main window in a 25 subwindows and draw something
+We split the main window in a 25 subwindows and draw something
in specific windows numbers.
Then open an independent window and draw a shape on it.
"""
@@ -23,13 +23,13 @@
c = vp1.load(datadir+"shapes/atc.ply")
# show a text in each renderer
-for i in range(22):
- txt = Text("renderer\nnr."+str(i), c=i, s=0.5, font='arial')
- vp1.show(txt, at=i)
+for i in range(25):
+ txt = Text("renderer\nnr."+str(i), c='k', s=0.5, font='arial')
+ vp1.show(txt, at=i, interactive=0)
vp1.show(a, at=22)
vp1.show(b, at=23)
-vp1.show(c, at=24)
+vp1.show(c, at=24, interactive=0)
############################################################
diff --git a/examples/basic/shrink.py b/examples/basic/shrink.py
index 54202811..ce05a72f 100644
--- a/examples/basic/shrink.py
+++ b/examples/basic/shrink.py
@@ -1,5 +1,5 @@
"""
-Shrink the triangulation of a mesh
+Shrink the triangulation of a mesh
to make the inside visible.
"""
from vtkplotter import load, Sphere, show, Text, datadir
diff --git a/examples/basic/text_just.py b/examples/basic/text_just.py
index 2a03d415..8c610286 100644
--- a/examples/basic/text_just.py
+++ b/examples/basic/text_just.py
@@ -11,4 +11,4 @@
to = Point([1, 2, 0], c="r") # mark text origin
ax = Point([0, 0, 0]) # mark axes origin
-show(tx, to, ax, axes=8, verbose=0)
+show(tx, to, ax, axes=8)
diff --git a/examples/other/dolfin/README.md b/examples/other/dolfin/README.md
index b4975429..0a878040 100644
--- a/examples/other/dolfin/README.md
+++ b/examples/other/dolfin/README.md
@@ -17,9 +17,13 @@ python example.py # on mac OSX try 'pythonw' instead
|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:-----|
| [![showmesh](https://user-images.githubusercontent.com/32848391/53026243-d2d31900-3462-11e9-9dde-518218c241b6.jpg)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex01_show-mesh.py)
`ex01_show-mesh.py` | Show dolfin meshes in different ways. |
| | |
+| [![submesh](https://user-images.githubusercontent.com/32848391/56675428-4e984e80-66bc-11e9-90b0-43dde7e4cc29.png)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/demo_submesh.py)
`demo_submesh.py` | How to extract matching sub meshes from a common mesh. |
+| | |
+| [![pi_estimate](https://user-images.githubusercontent.com/32848391/56675429-4e984e80-66bc-11e9-9217-a0652a8e74fe.png)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/pi_estimate.py)
`pi_estimate.py` | Estimate _pi_ by integrating a circle surface. Latex formulas can be added to the renderer directly. |
+| | |
| [![tet_mesh](https://user-images.githubusercontent.com/32848391/53026244-d2d31900-3462-11e9-835a-1fa9d66d3dae.png)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex02_tetralize-mesh.py)
`ex02_tetralize-mesh.py` | Tetrahedral meshes generation with package _mshr_. |
| | |
-| [![poisson](https://user-images.githubusercontent.com/32848391/53044914-95838100-348c-11e9-8e55-e4732b742006.png)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex03_poisson.py)
`ex03_poisson.py` | Solving Poisson equation with Dirichlet conditions. |
+| [![poisson](https://user-images.githubusercontent.com/32848391/54925524-bec18200-4f0e-11e9-9eab-29fd61ef3b8e.png)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex03_poisson.py)
`ex03_poisson.py` | Solving Poisson equation with Dirichlet conditions. |
| | |
| [![mixpoisson](https://user-images.githubusercontent.com/32848391/53045761-b220b880-348e-11e9-840f-94c5c0e86668.png)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex04_mixed-poisson.py)
`ex04_mixed-poisson.py` | Solving Poisson equation using a mixed (two-field) formulation. |
| | |
@@ -38,4 +42,9 @@ python example.py # on mac OSX try 'pythonw' instead
| [![ft02](https://user-images.githubusercontent.com/32848391/55499287-ed91d380-5645-11e9-8e9a-e31e2e3b1649.jpg)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ft02_poisson_membrane.py)
`ft02_poisson_membrane.py` | Deflection of a membrane by a gaussian load. |
| | |
| [![ft04](https://user-images.githubusercontent.com/32848391/55578167-88a5ae80-5715-11e9-84ea-bdab54099887.gif)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ft04_heat_gaussian.py)
`ft04_heat_gaussian.py` | Diffusion of a Gaussian hill on a square domain. |
-
+| | |
+| [![cahn](https://user-images.githubusercontent.com/32848391/56664730-edb34b00-66a8-11e9-9bf3-73431f2a98ac.gif)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/demo_cahn-hilliard.py)
`demo_cahn-hilliard.py` | Solution of a particular nonlinear time-dependent fourth-order equation, known as the Cahn-Hilliard equation. |
+| | |
+| [![navier-stokes_lshape](https://user-images.githubusercontent.com/32848391/56671156-6bc91f00-66b4-11e9-8c58-e6b71e2ad1d0.gif)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/navier-stokes_lshape.py)
`navier-stokes_lshape.py` | Solve the incompressible Navier-Stokes equations on an L-shaped domain using Chorin's splitting method. |
+| | |
+| [![turing_pattern](https://user-images.githubusercontent.com/32848391/56056437-77cfeb00-5d5c-11e9-9887-828e5745d547.gif)](https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/turing_pattern.py)
`turing_pattern.py` | Solve a reaction-diffusion problem on a 2D domain. |
diff --git a/examples/other/dolfin/collisions.py b/examples/other/dolfin/collisions.py
index 12127441..404acd36 100644
--- a/examples/other/dolfin/collisions.py
+++ b/examples/other/dolfin/collisions.py
@@ -1,12 +1,12 @@
'''
compute_collision() will compute the collision of all the entities with
a Point while compute_first_collision() will always return its first entry.
-Especially if a point is on an element edge this can be tricky.
+Especially if a point is on an element edge this can be tricky.
You may also want to compare with the Cell.contains(Point) tool.
'''
# Script by Rudy at https://fenicsproject.discourse.group/t/
# any-function-to-determine-if-the-point-is-in-the-mesh/275/3
-import dolfin
+import dolfin
from vtkplotter.dolfin import shapes, plot, printc
n = 4
diff --git a/examples/other/dolfin/demo_cahn-hilliard.py b/examples/other/dolfin/demo_cahn-hilliard.py
new file mode 100644
index 00000000..1d20da4d
--- /dev/null
+++ b/examples/other/dolfin/demo_cahn-hilliard.py
@@ -0,0 +1,107 @@
+"""
+Solution of a particular nonlinear time-dependent
+fourth-order equation, known as the Cahn-Hilliard equation.
+"""
+#https://fenicsproject.org/olddocs/dolfin/1.4.0/python/demo/documented
+import random
+from dolfin import *
+set_log_level(30)
+
+
+# Class representing the intial conditions
+class InitialConditions(UserExpression):
+ def __init__(self, **kwargs):
+ random.seed(2 + MPI.rank(MPI.comm_world))
+ super().__init__(**kwargs)
+ def eval(self, values, x):
+ values[0] = 0.63 + 0.02 * (0.5 - random.random())
+ values[1] = 0.0
+ def value_shape(self): return (2,)
+
+# Class for interfacing with the Newton solver
+class CahnHilliardEquation(NonlinearProblem):
+ def __init__(self, a, L):
+ NonlinearProblem.__init__(self)
+ self.L = L
+ self.a = a
+ def F(self, b, x): assemble(self.L, tensor=b)
+ def J(self, A, x): assemble(self.a, tensor=A)
+
+# Model parameters
+lmbda = 1.0e-02 # surface parameter
+dt = 5.0e-06 # time step
+# time stepping family,
+# e.g. theta=1 -> backward Euler, theta=0.5 -> Crank-Nicolson
+theta = 0.5
+
+# Form compiler options
+parameters["form_compiler"]["optimize"] = True
+parameters["form_compiler"]["cpp_optimize"] = True
+
+# Create mesh and define function spaces
+mesh = UnitSquareMesh(60, 60)
+# mesh = UnitSquareMesh.create(60, 60, CellType.Type.triangle)
+# V = FunctionSpace(mesh, "Lagrange", 1)
+P1 = FiniteElement("Lagrange", mesh.ufl_cell(), 1)
+ME = FunctionSpace(mesh, P1 * P1)
+
+# Define trial and test functions
+du = TrialFunction(ME)
+q, v = TestFunctions(ME)
+
+# Define functions
+u = Function(ME) # current solution
+u0 = Function(ME) # solution from previous converged step
+
+# Split mixed functions
+dc, dmu = split(du)
+c, mu = split(u)
+c0, mu0 = split(u0)
+
+# Create intial conditions and interpolate
+u_init = InitialConditions(degree=1)
+u.interpolate(u_init)
+u0.interpolate(u_init)
+
+# Compute the chemical potential df/dc
+c = variable(c)
+f = 100 * c ** 2 * (1 - c) ** 2
+mu_mid = (1 - theta) * mu0 + theta * mu
+
+# Weak statement of the equations
+L0 = c * q - c0 * q + dt * dot(grad(mu_mid), grad(q))
+L1 = mu * v - diff(f, c) * v - lmbda * dot(grad(c), grad(v))
+L = (L0 + L1) * dx
+
+# Compute directional derivative about u in the direction of du
+a = derivative(L, u, du)
+
+# Create nonlinear problem and Newton solver
+problem = CahnHilliardEquation(a, L)
+solver = NewtonSolver()
+solver.parameters["linear_solver"] = "lu"
+solver.parameters["convergence_criterion"] = "incremental"
+solver.parameters["relative_tolerance"] = 1e-6
+
+# Step in time
+from vtkplotter.dolfin import plot
+
+t = 0
+T = 10*dt
+while t < T:
+ t += dt
+ u0.vector()[:] = u.vector()
+ solver.solve(problem, u.vector())
+
+ plot(u.split()[0],
+ z=t*2e4,
+ add=True, # do not clear the canvas
+ style=0,
+ lw=0,
+ scalarbar='h',
+ elevation=-3, # move camera a bit
+ azimuth=1,
+ text='time: '+str(t*2e4),
+ interactive=0 )
+
+plot()
diff --git a/examples/other/dolfin/demo_submesh.py b/examples/other/dolfin/demo_submesh.py
new file mode 100644
index 00000000..2fec3276
--- /dev/null
+++ b/examples/other/dolfin/demo_submesh.py
@@ -0,0 +1,40 @@
+"""
+how to extract matching
+sub meshes from a common mesh.
+"""
+from dolfin import *
+
+class Structure(SubDomain):
+ def inside(self, x, on_boundary):
+ return x[0] > 1.4 - DOLFIN_EPS and x[0] < 1.6 \
+ + DOLFIN_EPS and x[1] < 0.6 + DOLFIN_EPS
+
+mesh = RectangleMesh(Point(0.0, 0.0), Point(3.0, 1.0), 60, 20)
+
+# Create sub domain markers and mark everaything as 0
+sub_domains = MeshFunction("size_t", mesh, mesh.topology().dim())
+sub_domains.set_all(0)
+
+# Mark structure domain as 1
+structure = Structure()
+structure.mark(sub_domains, 1)
+
+# Extract sub meshes
+fluid_mesh = SubMesh(mesh, sub_domains, 0)
+structure_mesh = SubMesh(mesh, sub_domains, 1)
+
+# Move structure mesh
+for x in structure_mesh.coordinates():
+ x[0] += 0.1*x[0]*x[1]
+
+# Move fluid mesh according to structure mesh
+ALE.move(fluid_mesh, structure_mesh)
+fluid_mesh.smooth()
+
+#############################################
+from vtkplotter.dolfin import plot
+
+plot(fluid_mesh, text=__doc__, interactive=False)
+plot(structure_mesh, c='tomato', add=True)
+plot()
+
diff --git a/examples/other/dolfin/elastodynamics.py b/examples/other/dolfin/elastodynamics.py
index 5add1001..c6bd03c7 100644
--- a/examples/other/dolfin/elastodynamics.py
+++ b/examples/other/dolfin/elastodynamics.py
@@ -181,12 +181,13 @@ def local_project(v, V, u=None):
return
################################################################### time loop
-from vtkplotter.dolfin import plot, ProgressBar, screenshot, shapes
+from vtkplotter.dolfin import *
+from vtkplotter import Box
# add a frame box
-box = shapes.Box(length=1, width=1, height=1).pos(0.5,0,0).wireframe()
+box = Box(length=1, width=1, height=1).pos(0.5,0,0).wireframe()
-tex = shapes.Latex(r'\nabla \cdot \sigma+\rho b=\rho \ddot{u}', s=.2).pos(.4,.4,-.5)
+tex = Latex(r'\nabla \cdot \sigma+\rho b=\rho \ddot{u}', s=.2).pos(.4,.4,-.5)
pb = ProgressBar(0, len(np.diff(time)), c='blue')
@@ -222,20 +223,19 @@ def local_project(v, V, u=None):
E_tot = E_elas+E_kin+E_damp #-E_ext
energies[i+1, :] = np.array([E_elas, E_kin, E_damp, E_tot])
- plot(u, box, tex,
- mode='warped mesh',
- style='matplotlib',
+ plot(u, box, tex,
+ mode='warped mesh',
+ style='matplotlib',
axes=0, # no axes
- scalarbar=False,
+ scalarbar=False,
azimuth=1, # at each iteration add an angle to rotate scene
- text=__doc__,
+ text=__doc__, # add this file header
interactive=False)
#screenshot('bar'+str(i)+'.png') # uncomment to save screenshots
pb.print("Time: "+str(t)+" seconds")
-plot(interactive=True)
+plot()
-# \nabla \cdot \sigma+\rho b=\rho \ddot{u}
diff --git a/examples/other/dolfin/ex01_show-mesh.py b/examples/other/dolfin/ex01_show-mesh.py
index ffb3a2c4..0bc47475 100644
--- a/examples/other/dolfin/ex01_show-mesh.py
+++ b/examples/other/dolfin/ex01_show-mesh.py
@@ -5,12 +5,12 @@
import dolfin
from vtkplotter.dolfin import plot, datadir
-
mesh1 = dolfin.Mesh(datadir + "dolfin_fine.xml")
-plot(mesh1)
+plot(mesh1)
-# show another light-green mesh in a new plotter window,
+# show another light-green mesh in a new plotter window,
# show file header too as an additional text comment
mesh2 = dolfin.UnitCubeMesh(8,8,8)
+
plot(mesh2, text=__doc__, color='lg', newPlotter=True)
diff --git a/examples/other/dolfin/ex03_poisson.py b/examples/other/dolfin/ex03_poisson.py
index b430a8ca..1a24e77d 100644
--- a/examples/other/dolfin/ex03_poisson.py
+++ b/examples/other/dolfin/ex03_poisson.py
@@ -30,9 +30,9 @@
f = r'-\nabla^{2} u=f'
########################################################### vtkplotter
-from vtkplotter.dolfin import plot, Text, Latex, clear
+from vtkplotter.dolfin import plot, Latex, clear
-l = Latex(f, s=.2, c='w').addPos(.6,.6,.1)
+l = Latex(f, s=0.2, c='w').addPos(.6,.6,.1)
plot(u, l, cmap='jet', scalarbar='h', text=__doc__)
diff --git a/examples/other/dolfin/ex04_mixed-poisson.py b/examples/other/dolfin/ex04_mixed-poisson.py
index 2756961b..e7636494 100644
--- a/examples/other/dolfin/ex04_mixed-poisson.py
+++ b/examples/other/dolfin/ex04_mixed-poisson.py
@@ -1,5 +1,5 @@
"""
-Solving Poisson equation using
+Solving Poisson equation using
a mixed (two-field) formulation.
"""
# needs python3
diff --git a/examples/other/dolfin/ex05_non-matching-meshes.py b/examples/other/dolfin/ex05_non-matching-meshes.py
index cc877379..22bfdf22 100644
--- a/examples/other/dolfin/ex05_non-matching-meshes.py
+++ b/examples/other/dolfin/ex05_non-matching-meshes.py
@@ -1,5 +1,5 @@
"""
-Interpolate functions between
+Interpolate functions between
finite element spaces on non-matching meshes.
"""
# https://fenicsproject.org/docs/dolfin/2018.1.0/python/demos
diff --git a/examples/other/dolfin/ex06_elasticity2.py b/examples/other/dolfin/ex06_elasticity2.py
index b62ec717..2b4b5cf2 100644
--- a/examples/other/dolfin/ex06_elasticity2.py
+++ b/examples/other/dolfin/ex06_elasticity2.py
@@ -39,7 +39,7 @@
from vtkplotter.dolfin import plot, printc
# print out some funny text
-printc("""~idea Try out plot options:
+printc("""~idea Try out plot options:
~pin color='gold'
~pin alpha=0.2, depthpeeling=True
~pin mode='mesh warp lines', lw=.05""", c='blue')
diff --git a/examples/other/dolfin/ex07_stokes-iterative.py b/examples/other/dolfin/ex07_stokes-iterative.py
index f639e37b..88b0bd08 100644
--- a/examples/other/dolfin/ex07_stokes-iterative.py
+++ b/examples/other/dolfin/ex07_stokes-iterative.py
@@ -71,7 +71,7 @@ def top_bottom(x, on_boundary):
from vtkplotter.dolfin import plot, printHistogram
# Plot u and p solutions on N=2 synced renderers
-plot(u, mode='mesh arrows', at=0, N=2, legend='velocity', bg='white',
+plot(u, mode='mesh arrows', at=0, N=2, legend='velocity', bg='white',
scale=0.1, wireframe=1, lw=0.03, alpha=0.5, scalarbar=False)
printHistogram(pressures, title='pressure histo', logscale=True, c=1)
diff --git a/examples/other/dolfin/ft04_heat_gaussian.py b/examples/other/dolfin/ft04_heat_gaussian.py
index dd3d108f..07abe425 100644
--- a/examples/other/dolfin/ft04_heat_gaussian.py
+++ b/examples/other/dolfin/ft04_heat_gaussian.py
@@ -44,8 +44,9 @@ def boundary(x, on_boundary):
############################################################# vtkplotter
from vtkplotter.dolfin import plot, Latex
-f = r'\frac{\partial u}{\partial t}=\nabla^{2} u+f~\mathrm{in}~\Omega\times(0,T]'
-formula = Latex(f, pos=(-.2,-1, .1), s=.5, bg='w', alpha=.7)
+f = r'\frac{\partial u}{\partial t}=\nabla^2 u+f~\mathrm{in}~\Omega\times(0,T]'
+formula = Latex(f, pos=(-.4,-.8, .1), s=0.6, c='w')
+formula.crop(0.2, 0.4) # crop top and bottom 20% and 40%
# Time-stepping
u = Function(V)
@@ -55,9 +56,9 @@ def boundary(x, on_boundary):
solve(a == L, u, bc)
# Plot solution
- plot(u, formula, interactive=False, scalarbar=False)
+ plot(u, formula, scalarbar=False, interactive=False)
# Update previous solution
u_n.assign(u)
-plot(u, interactive=True)
+plot()
diff --git a/examples/other/dolfin/ft07_navier_stokes_channel.py b/examples/other/dolfin/ft07_navier_stokes_channel.py
index 007ea1c4..fed34fac 100644
--- a/examples/other/dolfin/ft07_navier_stokes_channel.py
+++ b/examples/other/dolfin/ft07_navier_stokes_channel.py
@@ -7,7 +7,6 @@
div(u) = 0
"""
from fenics import *
-import numpy as np
T = 10.0 # final time
num_steps = 90 # number of time steps
@@ -110,13 +109,13 @@ def sigma(u, p):
p_n.assign(p_)
# Plot solution
- plot(u_,
+ plot(u_,
cmap='plasma',
scalarbar=False,
bg='w',
+ text=__doc__,
axes=7, # bottom ruler
- interactive=False,
- )
+ interactive=False)
-plot(u_, cmap='plasma', text=__doc__, interactive=True)
+plot()
diff --git a/examples/other/dolfin/ft08_navier_stokes_cylinder.py b/examples/other/dolfin/ft08_navier_stokes_cylinder.py
index 887bbee4..198148f5 100644
--- a/examples/other/dolfin/ft08_navier_stokes_cylinder.py
+++ b/examples/other/dolfin/ft08_navier_stokes_cylinder.py
@@ -9,7 +9,6 @@
from __future__ import print_function, division
from fenics import *
from mshr import *
-import numpy as np
T = 10.0 # final time
num_steps = 50 # number of time steps
@@ -147,12 +146,12 @@ def sigma(u, p):
print('n:',n)
# Plot solution
- plot(u_,
+ plot(u_,
cmap='bone',
- scalarbar=False,
bg='w',
+ text=__doc__,
axes=0, # no axes
- interactive=False,
- )
+ scalarbar='h',
+ interactive=False)
-plot(u_, cmap='bone', scalarbar='h', title='Velocity', text=__doc__, interactive=True)
+plot()
diff --git a/examples/other/dolfin/ft09_reaction_system.py b/examples/other/dolfin/ft09_reaction_system.py
index 39edceb5..49e7e6f4 100644
--- a/examples/other/dolfin/ft09_reaction_system.py
+++ b/examples/other/dolfin/ft09_reaction_system.py
@@ -1,6 +1,6 @@
"""Convection-diffusion-reaction for a system
describing the concentration of three species A, B, C undergoing a simple
-first-order reaction A + B --> C with first-order decay of C.
+first-order reaction A + B --> C with first-order decay of C.
The velocity
is given by the flow field w from the demo navier_stokes_cylinder.py.
@@ -9,6 +9,7 @@
u_3' + w . nabla(u_3) - div(eps*grad(u_3)) = f_3 + K*u_1*u_2 - K*u_3
"""
+print(__doc__)
from fenics import *
set_log_level(30)
@@ -79,7 +80,7 @@
solve(F == 0, u)
_u_1, _u_2, _u_3 = u.split()
-
+
# Update previous solution
u_n.assign(u)
@@ -91,7 +92,6 @@
scalarbar=False,
bg='w',
axes=0,
- text=__doc__,
zoom=2,
interactive=False)
diff --git a/examples/other/dolfin/navier-stokes_lshape.py b/examples/other/dolfin/navier-stokes_lshape.py
new file mode 100644
index 00000000..92886950
--- /dev/null
+++ b/examples/other/dolfin/navier-stokes_lshape.py
@@ -0,0 +1,116 @@
+"""
+Solve the incompressible Navier-Stokes equations
+on an L-shaped domain using Chorin's splitting method.
+"""
+from dolfin import *
+from vtkplotter.dolfin import ProgressBar, plot, datadir
+
+# Print log messages only from the root process in parallel
+parameters["std_out_all_processes"] = False
+
+# Load mesh from file
+mesh = Mesh(datadir + "lshape.xml.gz")
+
+# Define function spaces (P2-P1)
+V = VectorFunctionSpace(mesh, "Lagrange", 2)
+Q = FunctionSpace(mesh, "Lagrange", 1)
+
+# Define trial and test functions
+u = TrialFunction(V)
+p = TrialFunction(Q)
+v = TestFunction(V)
+q = TestFunction(Q)
+
+# Set parameter values
+dt = 0.01
+T = 3
+nu = 0.01
+
+# Define time-dependent pressure boundary condition
+p_in = Expression("sin(3.0*t)", t=0.0, degree=2)
+
+# Define boundary conditions
+noslip = DirichletBC(V, (0, 0), "on_boundary && \
+ (x[0] < DOLFIN_EPS | x[1] < DOLFIN_EPS | \
+ (x[0] > 0.5 - DOLFIN_EPS && x[1] > 0.5 - DOLFIN_EPS))")
+inflow = DirichletBC(Q, p_in, "x[1] > 1.0 - DOLFIN_EPS")
+outflow = DirichletBC(Q, 0, "x[0] > 1.0 - DOLFIN_EPS")
+bcu = [noslip]
+bcp = [inflow, outflow]
+
+# Create functions
+u0 = Function(V)
+u1 = Function(V)
+p1 = Function(Q)
+
+# Define coefficients
+k = Constant(dt)
+f = Constant((0, 0))
+
+# Tentative velocity step
+F1 = (1/k)*inner(u - u0, v)*dx + inner(grad(u0)*u0, v)*dx + \
+ nu*inner(grad(u), grad(v))*dx - inner(f, v)*dx
+a1 = lhs(F1)
+L1 = rhs(F1)
+
+# Pressure update
+a2 = inner(grad(p), grad(q))*dx
+L2 = -(1/k)*div(u1)*q*dx
+
+# Velocity update
+a3 = inner(u, v)*dx
+L3 = inner(u1, v)*dx - k*inner(grad(p1), v)*dx
+
+# Assemble matrices
+A1 = assemble(a1)
+A2 = assemble(a2)
+A3 = assemble(a3)
+
+# Use amg preconditioner if available
+prec = "amg" if has_krylov_solver_preconditioner("amg") else "default"
+
+# Use nonzero guesses - essential for CG with non-symmetric BC
+parameters['krylov_solver']['nonzero_initial_guess'] = True
+
+# Time-stepping
+pb = ProgressBar(0, T, dt, c='green')
+for t in pb.range():
+
+ # Update pressure boundary condition
+ p_in.t = t
+
+ # Compute tentative velocity step
+ b1 = assemble(L1)
+ [bc.apply(A1, b1) for bc in bcu]
+ solve(A1, u1.vector(), b1, "bicgstab", "default")
+
+ # Pressure correction
+ b2 = assemble(L2)
+ [bc.apply(A2, b2) for bc in bcp]
+ [bc.apply(p1.vector()) for bc in bcp]
+ solve(A2, p1.vector(), b2, "bicgstab", prec)
+
+ # Velocity correction
+ b3 = assemble(L3)
+ [bc.apply(A3, b3) for bc in bcu]
+ solve(A3, u1.vector(), b3, "bicgstab", "default")
+
+ # Move to next time step
+ u0.assign(u1)
+ t += dt
+
+ # Plot solution
+ plot(u1,
+ mode='mesh and arrows',
+ text="Velocity of fluid",
+ bg='w',
+ cmap='jet',
+ scale=0.3, # unit conversion factor
+ scalarbar=False,
+ resetcam=False,
+ interactive=False)
+ pb.print()
+
+plot()
+
+
diff --git a/examples/other/dolfin/noplot/ex02_tetralize-mesh.py b/examples/other/dolfin/noplot/ex02_tetralize-mesh.py
index f7588ab9..0eae8beb 100644
--- a/examples/other/dolfin/noplot/ex02_tetralize-mesh.py
+++ b/examples/other/dolfin/noplot/ex02_tetralize-mesh.py
@@ -1,5 +1,5 @@
"""
-Tetrahedral meshes generation with
+Tetrahedral meshes generation with
package mshr and dolfin.
You can then visualize the file with:
diff --git a/examples/other/dolfin/noplot/ex04_mixed-poisson.py b/examples/other/dolfin/noplot/ex04_mixed-poisson.py
index e2c348f0..d55829f9 100644
--- a/examples/other/dolfin/noplot/ex04_mixed-poisson.py
+++ b/examples/other/dolfin/noplot/ex04_mixed-poisson.py
@@ -1,5 +1,5 @@
"""
-How to solve Poisson equation using
+How to solve Poisson equation using
a mixed (two-field) formulation.
"""
# https://fenicsproject.org/docs/dolfin/2018.1.0/python/demos/mixed-poisson
diff --git a/examples/other/dolfin/noplot/ex05_non-matching-meshes.py b/examples/other/dolfin/noplot/ex05_non-matching-meshes.py
index 89434c63..6bfc1d7a 100644
--- a/examples/other/dolfin/noplot/ex05_non-matching-meshes.py
+++ b/examples/other/dolfin/noplot/ex05_non-matching-meshes.py
@@ -1,5 +1,5 @@
"""
-Interpolate functions between
+Interpolate functions between
finite element spaces on non-matching meshes.
"""
# https://fenicsproject.org/docs/dolfin/2018.1.0/python/demos
diff --git a/examples/other/dolfin/pi_estimate.py b/examples/other/dolfin/pi_estimate.py
index 800ca2f8..337694da 100644
--- a/examples/other/dolfin/pi_estimate.py
+++ b/examples/other/dolfin/pi_estimate.py
@@ -11,10 +11,10 @@
mesh = generate_mesh(domain, res)
A = assemble(Constant(1) * dx(domain=mesh))
printc("resolution = %i, \t A-pi = %.5e" % (res, A-pi))
-
+
printc('~pi is about', A, c='yellow')
l = Latex(r'\mathrm{Area}(r)=\pi=\int\int_D 1 \cdot d(x,y)', s=0.3)
l.crop(0.3,0.3).z(0.1) # crop invisible top and bottom and set at z=0.1
-plot(mesh, l, alpha=0.4, style=1, axes=3)
+plot(mesh, l, alpha=0.4, ztitle='', style=1, axes=3)
diff --git a/examples/other/dolfin/run_all.sh b/examples/other/dolfin/run_all.sh
index 87d171eb..9d603d13 100755
--- a/examples/other/dolfin/run_all.sh
+++ b/examples/other/dolfin/run_all.sh
@@ -4,6 +4,33 @@
printf "\033c"
echo Running examples in directory dolfin/
+##########################
+echo Running ascalarbar.py
+python ascalarbar.py
+
+echo Running collisions.py
+python collisions.py
+
+echo Running calc_surface_area.py
+python calc_surface_area.py
+
+echo Running markmesh.py
+python markmesh.py
+
+echo Running pi_estimate.py
+python pi_estimate.py
+
+echo Running submesh_boundary.py
+python submesh_boundary.py
+
+echo Running demo_submesh.py
+python demo_submesh.py
+
+echo Running elastodynamics.py
+python elastodynamics.py
+
+
+######################################
echo Running ex01_show-mesh.py
python ex01_show-mesh.py
@@ -29,30 +56,7 @@ echo Running ex07_stokes-iterative.py
python ex07_stokes-iterative.py
-
-##########################
-echo Running ascalarbar.py
-python ascalarbar.py
-
-echo Running collisions.py
-python collisions.py
-
-echo Running calc_surface_area.py
-python calc_surface_area.py
-
-echo Running markmesh.py
-python markmesh.py
-
-echo Running elastodynamics.py
-python elastodynamics.py
-
-echo Running pi_estimate.py
-python pi_estimate.py
-
-echo Running stokes.py
-python stokes.py
-
-
+######################################
echo Running ft02_poisson_membrane.py
python ft02_poisson_membrane.py
@@ -68,6 +72,15 @@ python ft08_navier_stokes_cylinder.py
echo Running ft09_reaction_system.py
python ft09_reaction_system.py
+echo Running stokes.py
+python stokes.py
+
+echo Running demo_cahn-hilliard.py
+python demo_cahn-hilliard.py
+
+echo Running turing.py
+python turing.py
+
######################################
echo
diff --git a/examples/other/dolfin/stokes.py b/examples/other/dolfin/stokes.py
index 14cff90b..e42b344e 100644
--- a/examples/other/dolfin/stokes.py
+++ b/examples/other/dolfin/stokes.py
@@ -50,6 +50,6 @@
f = r'-\nabla \cdot(\nabla u+p I)=f ~\mathrm{in}~\Omega'
formula = Latex(f, pos=(0.55,0.45,-.05), s=0.1)
-plot(u, formula, at=0, N=2, text="velocity", mode='mesh and arrows',
+plot(u, formula, at=0, N=2, text="velocity", mode='mesh and arrows',
scale=.03, wire=1, scalarbar=False, style=1)
plot(p, at=1, text="pressure", cmap='jet')
diff --git a/examples/other/dolfin/submesh_boundary.py b/examples/other/dolfin/submesh_boundary.py
new file mode 100644
index 00000000..f129bbf5
--- /dev/null
+++ b/examples/other/dolfin/submesh_boundary.py
@@ -0,0 +1,34 @@
+"""
+Extract submesh boundaries.
+"""
+# https://fenicsproject.discourse.group/t/output-parts-of-boundary/537
+from fenics import *
+from mshr import *
+from numpy import array
+from numpy.linalg import norm
+
+domain = Box(Point(0,0,0), Point(10,10,10)) - Sphere(Point(5,5,5), 3)
+mesh = generate_mesh(domain, 32)
+exterior = BoundaryMesh(mesh, "exterior")
+
+
+def inSphere(x):
+ v = x - array([5, 5, 5])
+ return norm(v) < 3 + 1e2 * DOLFIN_EPS
+
+class SphereDomain(SubDomain):
+ def inside(self, x, on_boundary):
+ return inSphere(x)
+
+class BoxDomain(SubDomain):
+ def inside(self, x, on_boundary):
+ return not inSphere(x)
+
+sph = SubMesh(exterior, SphereDomain())
+box = SubMesh(exterior, BoxDomain())
+
+
+from vtkplotter.dolfin import plot
+
+plot(sph, at=0, N=2, c='red', text=__doc__)
+plot(box, at=1, wireframe=True)
diff --git a/examples/other/dolfin/turing_pattern.py b/examples/other/dolfin/turing_pattern.py
new file mode 100644
index 00000000..eb496a01
--- /dev/null
+++ b/examples/other/dolfin/turing_pattern.py
@@ -0,0 +1,83 @@
+from dolfin import *
+from numpy.random import random
+set_log_level(30)
+
+
+class TuringPattern(NonlinearProblem):
+ def __init__(self, a, L):
+ NonlinearProblem.__init__(self)
+ self.L = L
+ self.a = a
+ def F(self, b, x): assemble(self.L, tensor=b)
+ def J(self, A, x): assemble(self.a, tensor=A)
+
+mesh = UnitSquareMesh(48, 48)
+
+U = FiniteElement("CG", mesh.ufl_cell(), 2)
+
+W = FunctionSpace(mesh, U * U)
+
+du = TrialFunction(W)
+q, p = TestFunctions(W)
+
+w = Function(W)
+w0 = Function(W)
+
+# Split mixed functions
+dact, dhib = split(du)
+act, hib = split(w)
+act0, hib0 = split(w0)
+
+dt = 0.04
+T = 20.0
+
+class IC(UserExpression):
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ def eval(self, values, x):
+ values[0] = 1.0*random() +0.25
+ values[1] = 1.0*random() +0.25
+ def value_shape(self): return (2,)
+
+w_init = IC(element=W.ufl_element(), degree=2)
+w.interpolate(w_init)
+w0.interpolate(w_init)
+
+L0 = act*q - act0*q \
+ + dt*0.0005*inner(grad(act), grad(q)) \
+ - dt*inner(act*act*hib,q) \
+ + 1.0*dt*inner(act,q)
+L1 = hib*p -hib0*p \
+ + dt*0.1*inner(grad(hib), grad(p)) \
+ + dt*inner(act*act*hib, p) \
+ - dt*inner(Constant(1.0),p)
+L = (L0 + L1) *dx
+
+# Compute directional derivative about u in the direction of du
+a = derivative(L, w, du)
+
+problem = TuringPattern(a, L)
+solver = NewtonSolver()
+solver.parameters["linear_solver"] = "lu"
+solver.parameters["convergence_criterion"] = "incremental"
+solver.parameters["relative_tolerance"] = 1e-2
+
+
+########################################### time steps
+from vtkplotter.dolfin import plot, printc
+
+t = 0
+printc('~bomb Press Esc to quit.', c='y', invert=True)
+while t < T:
+ t += dt
+ w0.vector()[:] = w.vector()
+ solver.solve(problem, w.vector())
+
+ plot(w.split()[0],
+ style=4,
+ lw=0,
+ scalarbar='h',
+ text='time: '+str(t),
+ interactive=0 )
+
+plot()
diff --git a/examples/other/makeVideo.py b/examples/other/makeVideo.py
index 9432c630..3ccf9222 100644
--- a/examples/other/makeVideo.py
+++ b/examples/other/makeVideo.py
@@ -1,6 +1,6 @@
"""
Make a video (needs ffmpeg)
- Set offscreen=True to only produce the video
+ Set offscreen=True to only produce the video
without any graphical window showing
"""
print(__doc__)
diff --git a/examples/run_all.sh b/examples/run_all.sh
index 0f93221c..14abe3e0 100755
--- a/examples/run_all.sh
+++ b/examples/run_all.sh
@@ -102,6 +102,9 @@ python basic/mesh_threshold.py
echo Running basic/mesh_modify.py
python basic/mesh_modify.py
+echo Running basic/mesh_map2cell.py
+python basic/mesh_map2cell.py
+
echo Running basic/pca.py
python basic/pca.py
@@ -157,7 +160,7 @@ echo Running basic/tube.py
python basic/tube.py
echo Running basic/boolean.py
-python basic/boolean.py # fails for vtk version<7
+python basic/boolean.py
echo Running basic/annotations.py
python basic/annotations.py
@@ -168,6 +171,9 @@ python basic/markpoint.py
echo Running basic/glyphs.py
python basic/glyphs.py
+echo Running basic/glyphs_arrows.py
+python basic/glyphs_arrows.py
+
#################################### advanced
echo Running advanced/fatlimb.py
diff --git a/examples/simulations/gyroscope2.py b/examples/simulations/gyroscope2.py
index f7a39797..0bafb87c 100644
--- a/examples/simulations/gyroscope2.py
+++ b/examples/simulations/gyroscope2.py
@@ -2,8 +2,8 @@
A gyroscope sitting on a pedestal.
The analysis is in terms of Lagrangian mechanics.
-The Lagrangian variables are polar angle theta,
-azimuthal angle phi, and spin angle psi.
+The Lagrangian variables are polar angle theta,
+azimuthal angle phi, and spin angle psi.
"""
# (adapted from http://www.glowscript.org)
from __future__ import division, print_function
diff --git a/examples/simulations/multiple_pendulum.py b/examples/simulations/multiple_pendulum.py
index af255bd1..0fb878c3 100644
--- a/examples/simulations/multiple_pendulum.py
+++ b/examples/simulations/multiple_pendulum.py
@@ -89,7 +89,7 @@
dist2 = (bob_x[i] - bob_x[j]) ** 2 + (bob_y[i] - bob_y[j]) ** 2
if dist2 < DiaSq: # are colliding
Ddist = np.sqrt(dist2) - 2 * R
- tau = versor(bob_x[j] - bob_x[i], bob_y[j] - bob_y[i])
+ tau = versor([bob_x[j] - bob_x[i], bob_y[j] - bob_y[i],0])
DR = Ddist / 2 * tau
bob_x[i] += DR[0] # DR.x
bob_y[i] += DR[1] # DR.y
diff --git a/examples/simulations/particle_simulator.py b/examples/simulations/particle_simulator.py
index f3b4e7c1..39eb6702 100644
--- a/examples/simulations/particle_simulator.py
+++ b/examples/simulations/particle_simulator.py
@@ -71,7 +71,7 @@ def __init__(self, pos, charge, mass, radius, color, vel, fixed, negligible):
radius: radius of the particle, in meters. No effect on simulation
color: color of the particle. If None, a default color will be chosen
vel: initial velocity vector, in m/s
- fixed: if True, particle will remain fixed in place
+ fixed: if True, particle will remain fixed in place
negligible: assume charge is small wrt other charges to speed up calculation
"""
self.pos = vector(pos)
diff --git a/examples/simulations/turing.py b/examples/simulations/turing.py
index 0c1f657e..09e7e154 100644
--- a/examples/simulations/turing.py
+++ b/examples/simulations/turing.py
@@ -1,7 +1,7 @@
"""
Scalar values are read from a file and represented
on a green scale on a mesh as a function of time.
-The difference between one time point and the next
+The difference between one time point and the next
is shown as a blue component.
"""
from __future__ import division, print_function
diff --git a/examples/simulations/wave_equation.py b/examples/simulations/wave_equation.py
index a2fb5be4..0c2ee71e 100644
--- a/examples/simulations/wave_equation.py
+++ b/examples/simulations/wave_equation.py
@@ -1,6 +1,6 @@
"""
Simulate a discrete collection of oscillators
-We will use this as a model of a vibrating string and
+We will use this as a model of a vibrating string and
compare two methods of integration: Euler and Runge-Kutta4.
For too large values of dt the simple Euler can diverge.
"""
diff --git a/examples/tutorial.py b/examples/tutorial.py
index 8dd8f560..e0589c5a 100644
--- a/examples/tutorial.py
+++ b/examples/tutorial.py
@@ -16,7 +16,6 @@
from __future__ import division, print_function
from random import gauss, uniform as u
from vtkplotter import *
-from vtkplotter import datadir
print(__doc__)
diff --git a/examples/volumetric/imageOperations.py b/examples/volumetric/imageOperations.py
index 5f8a0035..e2badda6 100644
--- a/examples/volumetric/imageOperations.py
+++ b/examples/volumetric/imageOperations.py
@@ -1,6 +1,6 @@
"""
Perform other simple mathematical operation between 3d images.
-Possible operations are: +, -, /, 1/x, sin, cos, exp, log, abs, **2, sqrt,
+Possible operations are: +, -, /, 1/x, sin, cos, exp, log, abs, **2, sqrt,
min, max, atan, atan2, median, mag, dot, gradient, divergence, laplacian.
Alphas defines the opacity transfer function in the scalar range.
"""
diff --git a/examples/volumetric/interpolateVolume.py b/examples/volumetric/interpolateVolume.py
index 2bae7a6b..ce70b5c0 100644
--- a/examples/volumetric/interpolateVolume.py
+++ b/examples/volumetric/interpolateVolume.py
@@ -1,53 +1,25 @@
"""
Generate a voxel dataset (vtkImageData) by interpolating a scalar
-which is only known on a scattered set of points.
-This is obtained by using RBF (radial basis function).
+which is only known on a scattered set of points or mesh.
+Available interpolation kernels are: shepard, gaussian, voronoi, linear
"""
-# @Author: Giovanni Dalmasso
-from __future__ import print_function
-import vtk
+# Author: Giovanni Dalmasso
from vtkplotter import *
import numpy as np
-bins = 30 # nr. of voxels per axis
-npts = 60 # nr. of points of known scalar value
+npts = 60 # nr. of points of known scalar value
+coords = np.random.rand(npts, 3) # range is [0, 1]
+scals = np.abs(coords[:, 2]) # let the scalar be the z of point itself
-img = vtk.vtkImageData()
-img.SetDimensions(bins, bins, bins) # range is [0, bins-1]
-img.AllocateScalars(vtk.VTK_FLOAT, 1)
+apts = Points(coords).addPointScalars(scals,'scals')
-coords = np.random.rand(npts, 3) # range is [0, 1]
-scals = np.abs(coords[:, 2]) # let the scalar be the z of point itself
-fact = 1.0 / (bins - 1) # conversion factor btw the 2 ranges
+img = interpolateToImageData(apts, kernel='shepard', radius=0.3)
-vp = Plotter(verbose=0, bg="white", axes=1)
-vp.ztitle = "z == scalar value"
-cloud = Points(coords)
+printHistogram(img, bins=25, c='b')
-# fill the vtkImageData object
-pb = ProgressBar(0, bins, c=4)
-for iz in pb.range():
- pb.print()
- for iy in range(bins):
- for ix in range(bins):
+#write(img, 'imgcube.vti')
- pt = vector(ix, iy, iz) * fact
- closestPointsIDs = cloud.closestPoint(pt, N=5, returnIds=True)
-
- num, den = 0, 0
- for i in closestPointsIDs: # work out RBF manually on N points
- invdist = 1 / (mag2(coords[i] - pt) + 1e-06)
- num += scals[i] * invdist
- den += invdist
- img.SetScalarComponentFromFloat(ix, iy, iz, 0, num / den)
-
-# vp.write(img, 'imgcube.tif') # or .vti
-
-# set colors and transparencies along the scalar range
vol = Volume(img, c=["r", "g", "b"], alphas=[0.4, 0.8]) # vtkVolume
-act = Points(coords / fact)
-
-printHistogram(vol, bins=25, c='b')
-vp.show(vol, act, Text(__doc__), viewup="z")
+show(apts, vol, Text(__doc__), bg="white", axes=8)
diff --git a/examples/volumetric/mesh2volume.py b/examples/volumetric/mesh2volume.py
index 74b874a4..cf530c93 100644
--- a/examples/volumetric/mesh2volume.py
+++ b/examples/volumetric/mesh2volume.py
@@ -6,6 +6,8 @@
"""
from vtkplotter import *
+doc = Text(__doc__, c="k")
+
s = load(datadir+"shapes/bunny.obj").normalize().wire()
img = actor2ImageData(s, spacing=(0.02, 0.02, 0.02))
@@ -14,6 +16,6 @@
iso = isosurface(img, smoothing=0.9).color("b")
-show(v, s.scale(1.05), Text(__doc__, c="k"), at=0, N=2, bg="w", verbose=0)
+show(v, s.scale(1.05), doc, at=0, N=2, bg="w")
show(iso, at=1, interactive=1)
diff --git a/examples/volumetric/probeLine.py b/examples/volumetric/probeLine.py
index 091813f0..0670c188 100644
--- a/examples/volumetric/probeLine.py
+++ b/examples/volumetric/probeLine.py
@@ -17,4 +17,4 @@
# print(a.scalars(0)) # numpy scalars can be access here
# print(a.scalars('vtkValidPointMask')) # the mask of valid points
-show(lines + [Text(__doc__)], axes=4, verbose=0, bg="w")
+show(lines, Text(__doc__), axes=4, bg="w")
diff --git a/examples/volumetric/probePlane.py b/examples/volumetric/probePlane.py
index 2b367ad6..7b1623f6 100644
--- a/examples/volumetric/probePlane.py
+++ b/examples/volumetric/probePlane.py
@@ -13,4 +13,4 @@
planes.append(a)
# print(max(a.scalars(0))) # access scalars this way, 0 means first
-show(planes + [Text(__doc__)], axes=4, verbose=0, bg="w")
+show(planes, Text(__doc__), axes=4, bg="w")
diff --git a/examples/volumetric/probePoints.py b/examples/volumetric/probePoints.py
index 1b32d4a4..ec39a7a0 100644
--- a/examples/volumetric/probePoints.py
+++ b/examples/volumetric/probePoints.py
@@ -10,6 +10,7 @@
apts = probePoints(img, pts).pointSize(3)
+#print(apts.scalars()) # check the list of point/cell scalars
scals = apts.scalars(0)
printHistogram(scals, minbin=1, horizontal=1, c='g')
diff --git a/examples/volumetric/readVolumeAsIsoSurface.py b/examples/volumetric/readVolumeAsIsoSurface.py
index edfb8498..71e87a73 100644
--- a/examples/volumetric/readVolumeAsIsoSurface.py
+++ b/examples/volumetric/readVolumeAsIsoSurface.py
@@ -31,4 +31,4 @@
a3 = load(datadir+"embryo.slc", c="g", smoothing=1, connectivity=1)
# newPlotter triggers the instantiation of a new Plotter object
-vp2 = show(a3, verbose=0, pos=(300, 300), newPlotter=True)
+vp2 = show(a3, pos=(300, 300), newPlotter=True)
diff --git a/examples/volumetric/read_vti.py b/examples/volumetric/read_vti.py
index b17c9540..e8606272 100644
--- a/examples/volumetric/read_vti.py
+++ b/examples/volumetric/read_vti.py
@@ -25,4 +25,4 @@
iso = load("data/vase.vti", threshold=140).wire(True).alpha(0.1)
# show command creates and returns an instance of class Plotter
-show(vol, iso, Text(__doc__), verbose=0, bg="w")
+show(vol, iso, Text(__doc__), bg="w")
diff --git a/examples/volumetric/signedDistance.py b/examples/volumetric/signedDistance.py
index 17484e5f..9a6e8b7e 100644
--- a/examples/volumetric/signedDistance.py
+++ b/examples/volumetric/signedDistance.py
@@ -1,7 +1,7 @@
"""
A mixed example with class vtkSignedDistance:
-generate a scalar field by the signed distance from a polydata,
+generate a scalar field by the signed distance from a polydata,
save it to stack.tif file,
then extract an isosurface from the 3d image.
"""
diff --git a/setup.py b/setup.py
index e5054510..f2b4adba 100644
--- a/setup.py
+++ b/setup.py
@@ -1,21 +1,29 @@
from setuptools import setup
+try:
+ VERSIONFILE = "vtkplotter/version.py"
+ verstrline = open(VERSIONFILE, "rt").read()
+ verstr = verstrline.split('=')[1].replace('\n','').replace("'","")
+except:
+ verstr='unknown_version'
+
+##############################################################
setup(
name='vtkplotter',
- version='2019.1.4', # change also in vtkplotter/__init__.py and docs/source/conf.py
+ version=verstr,
packages=['vtkplotter'],
scripts=['bin/vtkplotter', 'bin/vtkconvert'],
install_requires=['vtk'],
- description='''A python module for scientific visualization,
+ description='''A python module for scientific visualization,
analysis and animation of 3D objects and point clouds based on VTK.''',
- long_description="""A python module for scientific visualization,
+ long_description="""A python module for scientific visualization,
analysis and animation of 3D objects and point clouds based on VTK.
Check out https://vtkplotter.embl.es for documentation.""",
author='Marco Musy',
author_email='marco.musy@gmail.com',
license='MIT',
url='https://github.com/marcomusy/vtkplotter',
- keywords='vtk 3D visualization mesh',
+ keywords='vtk 3D visualization mesh numpy',
classifiers=['Intended Audience :: Science/Research',
'Intended Audience :: Education',
'Intended Audience :: Information Technology',
@@ -35,18 +43,18 @@
##############################################################
-# # check version number here and in vtkplotter/__init__.py docs/source/conf.py
-
# # check examples
+# change version in vtkplotter/version.py
# cd ~/Projects/vtkplotter/
-# pip install .
+# pip install .
# ( sudo -H pip install . )
# cd examples
# ./run_all.sh
# cd other/dolfin
-# ./run_all.sh
+# ./run_all.sh
# check vtkconvert:
# vtkconvert data/290.vtk -to ply
+# check on python2 the same
# git status
diff --git a/vtkplotter/__init__.py b/vtkplotter/__init__.py
index f35dd23f..6c23cabc 100644
--- a/vtkplotter/__init__.py
+++ b/vtkplotter/__init__.py
@@ -1,7 +1,7 @@
"""
.. image:: https://user-images.githubusercontent.com/32848391/46815773-dc919500-cd7b-11e8-8e80-8b83f760a303.png
-A python module for scientific visualization,
+A python module for scientific visualization,
analysis and animation of 3D objects and point clouds based on VTK.
.. note:: **Please check out the** `git repository `_.
@@ -21,24 +21,25 @@
Publications where ``vtkplotter`` has been used so far:
-1. Diego, X. *et al,*: *"Key features of Turing systems are determined purely by network topology"*,
-`Physical Review X 20 June 2018 `_.
+1. Diego, X. *et al,*: *"Key features of Turing systems are determined purely by network topology"*,
+`Physical Review X 20 June 2018 `_.
2. M. Musy, K. Flaherty, J. Raspopovic, A. Robert-Moreno, J. T. Richtsmeier, J. Sharpe:
*"A Quantitative Method for Staging Mouse Limb Embryos based on Limb Morphometry"*,
-Development 2018, `doi: 10.1242/dev.154856 `_,
+Development 2018, `doi: 10.1242/dev.154856 `_,
5 April 2018.
3. G. Dalmasso *et al.*, "Evolution in space and time of 3D volumetric images", in preparation.
**Have you found this software useful for your research? Please cite it as:**
-
-M. Musy, G. Dalmasso, & B. Sullivan.
-"`vtkplotter`, a python module for scientific visualization,
-analysis and animation of 3D objects and point clouds based on VTK." (version v8.9.0). Zenodo,
+
+M. Musy, et al.,
+"`vtkplotter`, a python module for scientific visualization,
+analysis and animation of 3D objects and point clouds based on VTK." (version v8.9.0). Zenodo,
`doi: 10.5281/zenodo.2561402 `_, 10 February 2019.
"""
from __future__ import print_function
+from vtkplotter.version import _version
__author__ = "Marco Musy"
__license__ = "MIT"
@@ -46,7 +47,7 @@
__email__ = "marco.musy@embl.es"
__status__ = "dev"
__website__ = "https://github.com/marcomusy/vtkplotter"
-__version__ = "2019.1.4" ### defined also above, in setup.py and docs/source/conf.py
+__version__ = _version
from vtkplotter.plotter import *
from vtkplotter.analysis import *
diff --git a/vtkplotter/actors.py b/vtkplotter/actors.py
index 2cb39ff5..4c313de1 100644
--- a/vtkplotter/actors.py
+++ b/vtkplotter/actors.py
@@ -9,7 +9,7 @@
__doc__ = (
"""
-Submodule extending the ``vtkActor``, ``vtkVolume``
+Submodule extending the ``vtkActor``, ``vtkVolume``
and ``vtkImageActor`` objects functionality.
"""
+ docs._defs
@@ -45,13 +45,16 @@ def mergeActors(actors, tol=0):
def collection():
"""
Return the list of actor which have been created so far,
- even without having assigned them a name.
- Useful in loops. E.g.:
+ without having to assign them a name.
+ Useful in loops.
- >>> from vtkplotter import Cone, collect, show
- >>> for i in range(10):
- >>> Cone(pos=[3*i, 0, 0], axis=[i, i-5, 0])
- >>> show(collection())
+ :Example:
+ .. code-block:: python
+
+ from vtkplotter import Cone, collection, show
+ for i in range(10):
+ Cone(pos=[3*i, 0, 0], axis=[i, i-5, 0])
+ show(collection())
"""
return settings.collectable_actors
@@ -111,6 +114,7 @@ def isosurface(image, smoothing=0, threshold=None, connectivity=False):
return a
+
################################################# classes
class Prop(object):
"""Adds functionality to ``Actor``, ``Assembly``,
@@ -129,6 +133,7 @@ def __init__(self):
self._time = 0
self._legend = None
self.scalarbar = None
+ self.renderedAt = set()
def show(
self,
@@ -470,6 +475,20 @@ def off(self):
"""Switch off actor visibility. Object is not removed."""
self.VisibilityOff()
return self
+
+ def box(self):
+ """Return the bounding box as a new ``Actor``.
+
+ .. hint:: |latex.py|_
+ """
+ b = self.GetBounds()
+ from vtkplotter.shapes import Box
+ pos = (b[0]+b[1])/2, (b[3]+b[2])/2, (b[5]+b[4])/2
+ length, width, height = b[1]-b[0], b[3]-b[2], b[5]-b[4]
+ oa = Box(pos, length, width, height, c='gray').wire()
+ if isinstance(self.GetProperty(), vtk.vtkProperty):
+ oa.SetProperty(self.GetProperty())
+ return oa
####################################################
@@ -515,6 +534,7 @@ def __init__(
self.point_locator = None
self.cell_locator = None
self.line_locator = None
+ self.scalarbar_actor = None
self._bfprop = None # backface property holder
self.GetProperty().SetInterpolationToFlat()
@@ -540,7 +560,8 @@ def __init__(
prp = self.GetProperty()
if settings.renderPointsAsSpheres:
- prp.RenderPointsAsSpheresOn()
+ if hasattr(prp, 'RenderPointsAsSpheresOn'):
+ prp.RenderPointsAsSpheresOn()
if alpha is not None:
prp.SetOpacity(alpha)
@@ -584,6 +605,10 @@ def __add__(self, actors):
return actors
return Assembly([self, actors])
+ #def __str__(self):
+ # utils.printInfo(self)
+ # return ""
+
def pickable(self, value=None):
"""Set/get pickable property of actor."""
if value is None:
@@ -594,7 +619,7 @@ def pickable(self, value=None):
def updateMesh(self, polydata):
"""
- Change or modify the polygonal mesh for the actor with a new one.
+ Overwrite the polygonal mesh of the actor with a new one.
"""
self.poly = polydata
self.mapper.SetInputData(polydata)
@@ -896,7 +921,7 @@ def bc(self, backColor=False):
def lineWidth(self, lw=None):
- """Set/get width of mesh edges. Same as lw()."""
+ """Set/get width of mesh edges. Same as `lw()`."""
if lw is not None:
if lw == 0:
self.GetProperty().EdgeVisibilityOff()
@@ -908,7 +933,7 @@ def lineWidth(self, lw=None):
return self
def lw(self, lineWidth=None):
- """Set/get width of mesh edges. Same as lineWidth()"""
+ """Set/get width of mesh edges. Same as `lineWidth()`."""
return self.lineWidth(lineWidth)
def clean(self, tol=None):
@@ -1086,7 +1111,7 @@ def distanceToMesh(self, actor, signed=False, negate=False):
'''
Computes the (signed) distance from one mesh to another.
- Example: |distance2mesh.py|_
+ .. hint:: |distance2mesh| |distance2mesh.py|_
'''
poly1 = self.polydata()
poly2 = actor.polydata()
@@ -1131,14 +1156,9 @@ def clone(self, transformed=True):
return cloned
- def transformPolydata(self, transformation):
- """Obsolete: use transformMesh()."""
- print("~noentry Obsolete transformPolydata(): use transformMesh().\n")
- return self.transformMesh(transformation)
-
def transformMesh(self, transformation):
"""
- Apply this transformation to the polygonal `mesh`,
+ Apply this transformation to the polygonal `mesh`,
not to the actor's transformation, which is reset.
:param transformation: ``vtkTransform`` or ``vtkMatrix4x4`` object.
@@ -1240,8 +1260,8 @@ def shrink(self, fraction=0.85):
Example:
.. code-block:: python
- from vtkplotter import load, Sphere, show
- pot = load('data/shapes/teapot.vtk').shrink(0.75)
+ from vtkplotter import *
+ pot = load(datadir + 'shapes/teapot.vtk').shrink(0.75)
s = Sphere(r=0.2).pos(0,0,-0.5)
show(pot, s)
@@ -1259,7 +1279,7 @@ def stretch(self, q1, q2):
.. hint:: |aspring| |aspring.py|_
- .. note:: for ``Actors`` like helices, Line, cylinders, cones etc.,
+ .. note:: for ``Actors`` like helices, Line, cylinders, cones etc.,
two attributes ``actor.base``, and ``actor.top`` are already defined.
"""
if self.base is None:
@@ -1290,11 +1310,6 @@ def stretch(self, q1, q2):
self.updateTrail()
return self
- def cutPlane(self, origin=(0, 0, 0), normal=(1, 0, 0), showcut=False):
- """Deprecated: use cutWithPlane"""
- colors.printc("~targetcut Plane deprecated: use cutWithPlane()", c=1, box="-")
- return self.cutWithPlane(origin, normal, showcut)
-
def cutWithPlane(self, origin=(0, 0, 0), normal=(1, 0, 0), showcut=False):
"""
Takes a ``vtkActor`` and cuts it with the plane defined by a point and a normal.
@@ -1302,6 +1317,16 @@ def cutWithPlane(self, origin=(0, 0, 0), normal=(1, 0, 0), showcut=False):
:param origin: the cutting plane goes through this point
:param normal: normal of the cutting plane
:param showcut: if `True` show the cut off part of the mesh as thin wireframe.
+
+ :Example:
+ .. code-block:: python
+
+ from vtkplotter import Cube
+
+ cube = Cube().cutWithPlane(normal=(1,1,1))
+ cube.bc('pink').show()
+
+ |cutcube|
.. hint:: |trail| |trail.py|_
"""
@@ -1477,6 +1502,33 @@ def triangle(self, verts=True, lines=True):
tf.Update()
return self.updateMesh(tf.GetOutput())
+ def mapCellsToPoints(self):
+ """
+ Transform cell data (i.e., data specified per cell)
+ into point data (i.e., data specified at cell points).
+ The method of transformation is based on averaging the data values
+ of all cells using a particular point.
+ """
+ c2p = vtk.vtkCellDataToPointData()
+ c2p.SetInputData(self.polydata(False))
+ c2p.Update()
+ return self.updateMesh(c2p.GetOutput())
+
+ def mapPointsToCells(self):
+ """
+ Transform point data (i.e., data specified per point)
+ into cell data (i.e., data specified per cell).
+ The method of transformation is based on averaging the data values
+ of all points defining a particular cell.
+
+ .. hint:: |mesh_map2cell| |mesh_map2cell.py|_
+
+ """
+ p2c = vtk.vtkPointDataToCellData()
+ p2c.SetInputData(self.polydata(False))
+ p2c.Update()
+ return self.updateMesh(p2c.GetOutput())
+
def pointColors(self, scalars, cmap="jet", alpha=1, bands=None, vmin=None, vmax=None):
"""
Set individual point colors by providing a list of scalar values and a color map.
@@ -1530,12 +1582,11 @@ def pointColors(self, scalars, cmap="jet", alpha=1, bands=None, vmin=None, vmax=
lut.Build()
for i, c in enumerate(cmap):
col = colors.getColor(c)
- if len(col) == 4:
- r, g, b, a = col
+ r, g, b = col
+ if useAlpha:
+ lut.SetTableValue(i, r, g, b, alpha[i])
else:
- r, g, b = col
- a = 1
- lut.SetTableValue(i, r, g, b, a)
+ lut.SetTableValue(i, r, g, b, alpha)
elif isinstance(cmap, vtk.vtkLookupTable):
sname = "pointColors_lut"
@@ -1605,18 +1656,18 @@ def cellColors(self, scalars, cmap="jet", alpha=1, bands=None, vmin=None, vmax=N
lut = vtk.vtkLookupTable() # build the look-up table
+
if utils.isSequence(cmap):
sname = "cellColors_custom"
lut.SetNumberOfTableValues(len(cmap))
lut.Build()
for i, c in enumerate(cmap):
col = colors.getColor(c)
- if len(col) == 4:
- r, g, b, a = col
+ r, g, b = col
+ if useAlpha:
+ lut.SetTableValue(i, r, g, b, alpha[i])
else:
- r, g, b = col
- a = 1
- lut.SetTableValue(i, r, g, b, a)
+ lut.SetTableValue(i, r, g, b, alpha)
elif isinstance(cmap, vtk.vtkLookupTable):
sname = "cellColors_lut"
@@ -1871,6 +1922,13 @@ def addGaussNoise(self, sigma):
Add gaussian noise.
:param float sigma: sigma is expressed in percent of the diagonal size of actor.
+
+ :Example:
+ .. code-block:: python
+
+ from vtkplotter import Sphere
+
+ Sphere().addGaussNoise(1.0).show()
"""
sz = self.diagonalSize()
pts = self.coordinates()
@@ -1940,11 +1998,13 @@ def smoothWSinc(self, niter=15, passBand=0.1, edgeAngle=15, featureAngle=60):
def fillHoles(self, size=None):
- """Identifies and fills holes in input mesh.
- Holes are identified by locating boundary edges, linking them together into loops,
- and then triangulating the resulting loops.
+ """Identifies and fills holes in input mesh.
+ Holes are identified by locating boundary edges, linking them together into loops,
+ and then triangulating the resulting loops.
:param float size: approximate limit to the size of the hole that can be filled.
+
+ Example: |fillholes.py|_
"""
fh = vtk.vtkFillHolesFilter()
if not size:
@@ -1959,6 +2019,8 @@ def fillHoles(self, size=None):
def write(self, filename="mesh.vtk", binary=True):
"""
Write actor's polydata in its current transformation to file.
+
+
"""
import vtkplotter.vtkio as vtkio
@@ -1969,7 +2031,7 @@ def write(self, filename="mesh.vtk", binary=True):
### stuff that is not returning the input (sometimes modified) actor ###
def normalAt(self, i):
- """Calculate normal at vertex point `i`."""
+ """Return the normal vector at vertex point `i`."""
normals = self.polydata(True).GetPointData().GetNormals()
return np.array(normals.GetTuple(i))
@@ -2015,10 +2077,10 @@ def polydata(self, transformed=True):
def coordinates(self, transformed=True, copy=True):
"""
- Return the list of vertex coordinates of the input mesh.
+ Return the list of vertex coordinates of the input mesh. Same as `actor.getPoints()`.
:param bool transformed: if `False` ignore any previous trasformation applied to the mesh.
- :param bool copy: if `False` return the reference to the points
+ :param bool copy: if `False` return the reference to the points
so that they can be modified in place.
.. hint:: |align1.py|_
@@ -2063,7 +2125,7 @@ def move(self, u_function):
def getTransform(self):
"""
Check if ``info['transform']`` exists and returns it.
- Otherwise return current user transformation
+ Otherwise return current user transformation
(where the actor is currently placed).
"""
if "transform" in self.info.keys():
@@ -2091,7 +2153,9 @@ def setTransform(self, T):
def isInside(self, point, tol=0.0001):
- """Return True if point is inside a polydata closed surface."""
+ """
+ Return True if point is inside a polydata closed surface.
+ """
poly = self.polydata(True)
points = vtk.vtkPoints()
points.InsertNextPoint(point)
@@ -2106,7 +2170,11 @@ def isInside(self, point, tol=0.0001):
return sep.IsInside(0)
def insidePoints(self, points, invert=False, tol=1e-05):
- """Return the sublist of points that are inside a polydata closed surface."""
+ """
+ Return the sublist of points that are inside a polydata closed surface.
+
+ .. hint:: |pca| |pca.py|_
+ """
poly = self.polydata(True)
# check if the stl file is closed
@@ -2245,9 +2313,20 @@ def connectedCells(self, index, returnIds=False):
def intersectWithLine(self, p0, p1):
- """Return the list of points intersecting the actor along segment p0 and p1.
+ """Return the list of points intersecting the actor
+ along the segment defined by two points `p0` and `p1`.
+
+ :Example:
+ .. code-block:: python
- .. hint:: |spherical_harmonics1.py|_ |spherical_harmonics2.py|_
+ from vtkplotter import *
+ s = Spring(alpha=0.2)
+ pts = s.intersectWithLine([0,0,0], [1,0.1,0])
+ ln = Line([0,0,0], [1,0.1,0], c='blue')
+ ps = Points(pts, r=10, c='r')
+ show(s, ln, ps, bg='white')
+
+ |intline|
"""
if not self.line_locator:
line_locator = vtk.vtkOBBTree()
@@ -2347,7 +2426,7 @@ def alpha(self, a=None):
else:
return self.GetProperty().GetOpacity()
- def crop(self, top=None, bottom=None, left=None, right=None):
+ def crop(self, top=None, bottom=None, right=None, left=None):
"""Crop image.
:param float top: fraction to crop from the top margin
@@ -2357,7 +2436,7 @@ def crop(self, top=None, bottom=None, left=None, right=None):
"""
extractVOI = vtk.vtkExtractVOI()
extractVOI.SetInputData(self.GetInput())
- extractVOI.IncludeBoundaryOn ()
+ extractVOI.IncludeBoundaryOn()
d = self.GetInput().GetDimensions()
bx0, bx1, by0, by1 = 0, d[0]-1, 0, d[1]-1
@@ -2368,7 +2447,7 @@ def crop(self, top=None, bottom=None, left=None, right=None):
extractVOI.SetVOI(bx0, bx1, by0, by1, 0, 0)
extractVOI.Update()
img = extractVOI.GetOutput()
- img.SetOrigin(-bx0, -by0, 0)
+ #img.SetOrigin(-bx0, -by0, 0)
self.GetMapper().SetInputData(img)
self.GetMapper().Modified()
return self
@@ -2420,10 +2499,10 @@ def __init__(self, img, c="blue", alphas=(0.0, 0.4, 0.9, 1)):
self.mapper.SetBlendModeToMaximumIntensity()
self.mapper.UseJitteringOn()
self.mapper.SetInputData(img)
- colors.printc("scalar range is", np.round(img.GetScalarRange(), 4), c="b", bold=0)
+ #colors.printc("scalar range is", np.round(img.GetScalarRange(), 4), c="b", bold=0)
smin, smax = img.GetScalarRange()
if smax > 1e10:
- print("~lightning Warning, high scalar range detected:", smax)
+ print("Warning, high scalar range detected:", smax)
smax = abs(10 * smin) + 0.1
print(" reset to:", smax)
@@ -2433,8 +2512,8 @@ def __init__(self, img, c="blue", alphas=(0.0, 0.4, 0.9, 1)):
r, g, b = colors.getColor(ci)
xalpha = smin + (smax - smin) * i / (len(c) - 1)
colorTransferFunction.AddRGBPoint(xalpha, r, g, b)
- colors.printc('\tcolor at', round(xalpha, 1),
- '\tset to', colors.getColorName((r, g, b)), c='b', bold=0)
+ #colors.printc('\tcolor at', round(xalpha, 1),
+ # '\tset to', colors.getColorName((r, g, b)), c='b', bold=0)
else:
# Create transfer mapping scalar value to color
r, g, b = colors.getColor(c)
@@ -2447,7 +2526,7 @@ def __init__(self, img, c="blue", alphas=(0.0, 0.4, 0.9, 1)):
xalpha = smin + (smax - smin) * i / (len(alphas) - 1)
# Create transfer mapping scalar value to opacity
opacityTransferFunction.AddPoint(xalpha, al)
- colors.printc(" alpha at", round(xalpha, 1), "\tset to", al, c="b", bold=0)
+ #colors.printc(" alpha at", round(xalpha, 1), "\tset to", al, c="b", bold=0)
# The property describes how the data will look
volumeProperty = vtk.vtkVolumeProperty()
@@ -2467,6 +2546,92 @@ def __init__(self, img, c="blue", alphas=(0.0, 0.4, 0.9, 1)):
self.SetMapper(self.mapper)
self.SetProperty(volumeProperty)
+
def imagedata(self):
+ """Return the ``vtkImagaData`` object."""
return self.image
+
+
+ def threshold(self, vmin=None, vmax=None, replaceWith=None):
+ """
+ Binary or continuous volume thresholding.
+ """
+ th = vtk.vtkImageThreshold()
+ th.SetInputData(self.image)
+
+ if vmin is not None and vmax is not None:
+ th.ThresholdBetween(vmin, vmax)
+ elif vmin is not None:
+ th.ThresholdByUpper(vmin)
+ elif vmax is not None:
+ th.ThresholdByLower(vmax)
+
+ if replaceWith:
+ th.ReplaceOutOn()
+ th.SetOutValue(replaceWith)
+ th.Update()
+
+ self.image = th.GetOutput()
+ self.mapper.SetInputData(self.image)
+ self.mapper.Modified()
+ return self
+
+ def crop(self, top=None, bottom=None, right=None, left=None, front=None, back=None):
+ """Crop a ``Volume``.
+
+ :param float top: fraction to crop from the top plane (positive z)
+ :param float bottom: fraction to crop from the bottom plane (negative z)
+ :param float front: fraction to crop from the front plane (positive y)
+ :param float back: fraction to crop from the back plane (negative y)
+ :param float right: fraction to crop from the right plane (positive x)
+ :param float left: fraction to crop from the left plane (negative x)
+ """
+ extractVOI = vtk.vtkExtractVOI()
+ extractVOI.SetInputData(self.image)
+ extractVOI.IncludeBoundaryOn()
+
+ d = self.image.GetDimensions()
+ bx0, bx1, by0, by1, bz0, bz1 = 0, d[0]-1, 0, d[1]-1, 0, d[2]-1
+ if left is not None: bx0 = int((d[0]-1)*left)
+ if right is not None: bx1 = int((d[0]-1)*(1-right))
+ if back is not None: by0 = int((d[1]-1)*back)
+ if front is not None: by1 = int((d[1]-1)*(1-front))
+ if bottom is not None: bz0 = int((d[2]-1)*bottom)
+ if top is not None: bz1 = int((d[2]-1)*(1-top))
+ extractVOI.SetVOI(bx0, bx1, by0, by1, bz0, bz1)
+ extractVOI.Update()
+ img = extractVOI.GetOutput()
+ #img.SetOrigin(-bx0, -by0, -bz0)
+ self.GetMapper().SetInputData(img)
+ self.GetMapper().Modified()
+ return self
+
+
+ def getVoxelsScalar(self, vmin=None, vmax=None):
+ """
+ Return voxel content as a ``numpy.array``.
+
+ :param float vmin: rescale scalar content to match `vmin` and `vmax` range.
+ :param float vmax: rescale scalar content to match `vmin` and `vmax` range.
+ """
+ nx, ny, nz = self.image.GetDimensions()
+
+ voxdata = np.zeros([nx, ny, nz])
+ lsx = range(nx)
+ lsy = range(ny)
+ lsz = range(nz)
+ renorm = vmin is not None and vmax is not None
+ for i in lsx:
+ for j in lsy:
+ for k in lsz:
+ s = self.image.GetScalarComponentAsFloat(i, j, k, 0)
+ if renorm:
+ s = (s - vmin) / (vmax - vmin)
+ voxdata[i, j, k] = s
+ return voxdata
+
+
+
+
+
diff --git a/vtkplotter/addons.py b/vtkplotter/addons.py
index f9adcf2c..c0f396be 100644
--- a/vtkplotter/addons.py
+++ b/vtkplotter/addons.py
@@ -6,23 +6,52 @@
import vtkplotter.shapes as shapes
from vtkplotter.actors import Assembly, Actor
import vtkplotter.utils as utils
-import vtkplotter.vtkio as vtkio
import vtkplotter.settings as settings
+import vtkplotter.docs as docs
import numpy
import vtk
-__all__ = []
+__doc__ = (
+ """
+Additional objects like axes, legends etc..
+"""
+ + docs._defs
+)
+
+__all__ = [
+ "addScalarBar",
+ "addScalarBar3D",
+ "addSlider2D",
+ "addSlider3D",
+ "addButton",
+ "addCutterTool",
+ "addIcon",
+ "addAxes",
+ "addFrame",
+ "addLegend",
+ ]
def addScalarBar(actor=None, c=None, title="", horizontal=False, vmin=None, vmax=None):
+ """Add a 2D scalar bar for the specified actor.
+
+ If `actor` is ``None`` will add it to the last actor in ``self.actors``.
+ .. hint:: |mesh_bands| |mesh_bands.py|_
+ """
vp = settings.plotter_instance
+
if actor is None:
actor = vp.lastActor()
+
if not hasattr(actor, "mapper"):
- colors.printc("~timesError in addScalarBar: input is not a Actor.", c=1)
+ colors.printc("~times Error in addScalarBar: input is not a Actor.", c=1)
return None
+ if vp and vp.renderer and actor.scalarbar_actor:
+ vp.renderer.RemoveActor(actor.scalarbar)
+
+
lut = actor.mapper.GetLookupTable()
if not lut:
return None
@@ -96,6 +125,7 @@ def addScalarBar(actor=None, c=None, title="", horizontal=False, vmin=None, vmax
sb.PickableOff()
vp.renderer.AddActor(sb)
vp.scalarbars.append(sb)
+ actor.scalarbar_actor = sb
vp.renderer.Render()
return sb
@@ -110,13 +140,28 @@ def addScalarBar3D(
nlabels=9,
ncols=256,
cmap=None,
- c="k",
+ c=None,
alpha=1,
):
+ """Draw a 3D scalar bar.
+
+ ``obj`` input can be:
+ - a list of numbers,
+ - a list of two numbers in the form `(min, max)`,
+ - a ``vtkActor`` already containing a set of scalars associated to vertices or cells,
+ - if ``None`` the last actor in the list of actors will be used.
+ .. hint:: |scalbar| |mesh_coloring.py|_
+ """
from vtk.util.numpy_support import vtk_to_numpy, numpy_to_vtk
vp = settings.plotter_instance
+ if c is None: # automatic black or white
+ c = (0.8, 0.8, 0.8)
+ if numpy.sum(colors.getColor(vp.backgrcol)) > 1.5:
+ c = (0.2, 0.2, 0.2)
+ c = colors.getColor(c)
+
gap = 0.4 # space btw nrs and scale
vtkscalars_name = ""
if obj is None:
@@ -143,7 +188,7 @@ def addScalarBar3D(
cmap = vtkscalars_name
# build the color scale part
- scale = shapes.Grid([-sx * gap, 0, 0], c="w", alpha=alpha, sx=sx, sy=sy, resx=1, resy=ncols)
+ scale = shapes.Grid([-sx * gap, 0, 0], c=c, alpha=alpha, sx=sx, sy=sy, resx=1, resy=ncols)
scale.GetProperty().SetRepresentationToSurface()
cscals = scale.cellCenters()[:, 1]
@@ -195,12 +240,26 @@ def _cellColors(scale, scalars, cmap, alpha):
vp.renderers[at].Render()
sact.PickableOff()
vp.scalarbars.append(sact)
+ if isinstance(obj, Actor):
+ obj.scalarbar_actor = sact
return sact
def addSlider2D(sliderfunc, xmin, xmax, value=None, pos=4, s=.04,
title='', c=None, showValue=True):
-
+ """Add a slider widget which can call an external custom function.
+
+ :param sliderfunc: external function to be called by the widget
+ :param float xmin: lower value
+ :param float xmax: upper value
+ :param float value: current value
+ :param list pos: position corner number: horizontal [1-4] or vertical [11-14]
+ it can also be specified by corners coordinates [(x1,y1), (x2,y2)]
+ :param str title: title text
+ :param bool showValue: if true current value is shown
+
+ .. hint:: |sliders| |sliders.py|_
+ """
vp = settings.plotter_instance
if c is None: # automatic black or white
c = (0.8, 0.8, 0.8)
@@ -313,6 +372,22 @@ def addSlider3D(
c=None,
showValue=True,
):
+ """Add a 3D slider widget which can call an external custom function.
+
+ :param sliderfunc: external function to be called by the widget
+ :param list pos1: first position coordinates
+ :param list pos2: second position coordinates
+ :param float xmin: lower value
+ :param float xmax: upper value
+ :param float value: initial value
+ :param float s: label scaling factor
+ :param str title: title text
+ :param c: slider color
+ :param float rotation: title rotation around slider axis
+ :param bool showValue: if True current value is shown
+
+ .. hint:: |sliders3d| |sliders3d.py|_
+ """
vp = settings.plotter_instance
if c is None: # automatic black or white
c = (0.8, 0.8, 0.8)
@@ -371,7 +446,7 @@ def addButton(
states=("On", "Off"),
c=("w", "w"),
bc=("dg", "dr"),
- pos=[20, 40],
+ pos=(20, 40),
size=24,
font="arial",
bold=False,
@@ -379,11 +454,26 @@ def addButton(
alpha=1,
angle=0,
):
-
+ """Add a button to the renderer window.
+
+ :param list states: a list of possible states ['On', 'Off']
+ :param c: a list of colors for each state
+ :param bc: a list of background colors for each state
+ :param pos: 2D position in pixels from left-bottom corner
+ :param size: size of button font
+ :param str font: font type (arial, courier, times)
+ :param bool bold: bold face (False)
+ :param bool italic: italic face (False)
+ :param float alpha: opacity level
+ :param float angle: anticlockwise rotation in degrees
+
+ .. hint:: |buttons| |buttons.py|_
+ """
vp = settings.plotter_instance
if not vp.renderer:
colors.printc("~timesError: Use addButton() after rendering the scene.", c=1)
return
+ import vtkplotter.vtkio as vtkio
bu = vtkio.Button(fnc, states, c, bc, pos, size, font, bold, italic, alpha, angle)
vp.renderer.AddActor2D(bu.actor)
vp.window.Render()
@@ -392,7 +482,10 @@ def addButton(
def addCutterTool(actor):
+ """Create handles to cut away parts of a mesh.
+ .. hint:: |cutter| |cutter.py|_
+ """
if isinstance(actor, vtk.vtkVolume):
return _addVolumeCutterTool(actor)
elif isinstance(actor, vtk.vtkImageData):
@@ -513,7 +606,14 @@ def ClipVolumeRender(obj, event):
def addIcon(iconActor, pos=3, size=0.08):
+ """Add an inset icon mesh into the same renderer.
+ :param pos: icon position in the range [1-4] indicating one of the 4 corners,
+ or it can be a tuple (x,y) as a fraction of the renderer size.
+ :param float size: size of the square inset.
+
+ .. hint:: |icon| |icon.py|_
+ """
vp = settings.plotter_instance
if not vp.renderer:
colors.printc("~lightningWarning: Use addIcon() after first rendering the scene.", c=3)
@@ -543,7 +643,22 @@ def addIcon(iconActor, pos=3, size=0.08):
def addAxes(axtype=None, c=None):
-
+ """Draw axes on scene. Available axes types:
+
+ :param int axtype:
+
+ - 0, no axes,
+ - 1, draw three gray grid walls
+ - 2, show cartesian axes from (0,0,0)
+ - 3, show positive range of cartesian axes from (0,0,0)
+ - 4, show a triad at bottom left
+ - 5, show a cube at bottom left
+ - 6, mark the corners of the bounding box
+ - 7, draw a simple ruler at the bottom of the window
+ - 8, show the ``vtkCubeAxesActor`` object
+ - 9, show the bounding box outLine
+ - 10, show three circles representing the maximum bounding box
+ """
vp = settings.plotter_instance
if axtype is not None:
vp.axes = axtype # overrride
@@ -619,13 +734,13 @@ def addAxes(axtype=None, c=None):
if len(vp.xtitle) == 1: # add axis length info
xtitle = vp.xtitle + " /" + utils.precision(sizes[0], 4)
wpos = [1 - (len(vp.xtitle) + 1) / 40, off, 0]
- xt = shapes.Text(xtitle, pos=wpos, normal=(0, 0, 1),
+ xt = shapes.Text(xtitle, pos=wpos, normal=(0, 0, 1),
s=0.025, c=c, justify="bottom-right")
if vp.ytitle:
if min_bns[2] <= 0 and max_bns[3] > 0: # mark y origin
oy = shapes.Cube([0, -min_bns[2] / sizes[1], 0], side=0.008, c=c)
- yt = shapes.Text(vp.ytitle, pos=(0, 0, 0), normal=(0, 0, 1),
+ yt = shapes.Text(vp.ytitle, pos=(0, 0, 0), normal=(0, 0, 1),
s=0.025, c=c, justify="bottom-right")
if len(vp.ytitle) == 1:
wpos = [off, 1 - (len(vp.ytitle) + 1) / 40, 0]
@@ -637,7 +752,7 @@ def addAxes(axtype=None, c=None):
if vp.ztitle:
if min_bns[4] <= 0 and max_bns[5] > 0: # mark z origin
oz = shapes.Cube([0, 0, -min_bns[4] / sizes[2]], side=0.008, c=c)
- zt = shapes.Text(vp.ztitle, pos=(0, 0, 0), normal=(1, -1, 0),
+ zt = shapes.Text(vp.ztitle, pos=(0, 0, 0), normal=(1, -1, 0),
s=0.025, c=c, justify="bottom-right")
if len(vp.ztitle) == 1:
wpos = [off * 0.6, off * 0.6, 1 - (len(vp.ztitle) + 1) / 40]
@@ -924,8 +1039,6 @@ def addFrame(c=None, alpha=0.5, bg=None, lw=0.5):
mapper.SetTransformCoordinate(cs)
fractor = vtk.vtkActor2D()
- fractor.GetPositionCoordinate().SetCoordinateSystemToNormalizedViewport()
- fractor.GetPosition2Coordinate().SetCoordinateSystemToNormalizedViewport()
fractor.GetPositionCoordinate().SetValue(0, 0)
fractor.GetPosition2Coordinate().SetValue(1, 1)
fractor.SetMapper(mapper)
diff --git a/vtkplotter/analysis.py b/vtkplotter/analysis.py
index 8f043180..5976a1ef 100644
--- a/vtkplotter/analysis.py
+++ b/vtkplotter/analysis.py
@@ -28,7 +28,6 @@
"delaunay3D",
"normalLines",
"extractLargestRegion",
- "align",
"alignLandmarks",
"alignICP",
"alignProcrustes",
@@ -60,6 +59,8 @@
"extractSurface",
"geometry",
"voronoi3D",
+ "connectedPoints",
+ "interpolateToImageData",
]
@@ -422,7 +423,7 @@ def histogram2D(xvalues, yvalues, bins=12, norm=1, c="g", alpha=1, fill=False):
def delaunay2D(plist, mode='xy', tol=None):
"""
Create a mesh from points in the XY plane.
- If `mode='fit'` then the filter computes a best fitting
+ If `mode='fit'` then the filter computes a best fitting
plane and projects the points onto it.
.. hint:: |delaunay2d| |delaunay2d.py|_
@@ -505,16 +506,16 @@ def extractLargestRegion(actor):
def alignLandmarks(source, target, rigid=False):
"""
- Find best matching of source points towards target
+ Find best matching of source points towards target
in the mean least square sense, in one single step.
"""
lmt = vtk.vtkLandmarkTransform()
ss = source.polydata().GetPoints()
st = target.polydata().GetPoints()
if source.N() != target.N():
- vc.printc('~times Error in alignLandmarks(): Source and Target with != nr of points!',
+ vc.printc('~times Error in alignLandmarks(): Source and Target with != nr of points!',
source.N(), target.N(), c=1)
- exit()
+ exit()
lmt.SetSourceLandmarks(ss)
lmt.SetTargetLandmarks(st)
if rigid:
@@ -532,12 +533,6 @@ def alignLandmarks(source, target, rigid=False):
return actor
-def align(source, target, iters=100, rigid=False):
- """Obsolete: use alignICP()."""
- vc.printc("~noentry Obsolete align(): use alignICP().", c=1)
- return alignICP(source, target, iters, rigid)
-
-
def alignICP(source, target, iters=100, rigid=False):
"""
Return a copy of source actor which is aligned to
@@ -739,7 +734,7 @@ def pcaEllipsoid(points, pvalue=0.95, pcaAxes=False):
"""
try:
from scipy.stats import f
- except:
+ except ImportError:
vc.printc("~times Error in Ellipsoid(): scipy not installed. Skip.", c=1)
return None
if isinstance(points, vtk.vtkActor):
@@ -868,8 +863,7 @@ def smoothMLS2D(actor, f=0.2, decimate=1, recursive=0, showNPlanes=0):
:param recursive: move points while algorithm proceedes.
:param showNPlanes: build an actor showing the fitting plane for N random points.
- .. hint:: |mesh_smoothers|
- |mesh_smoothers.py|_
+ .. hint:: |mesh_smoothers| |mesh_smoothers.py|_
|moving_least_squares2D| |moving_least_squares2D.py|_
@@ -1052,7 +1046,7 @@ def probePoints(img, pts):
"""
Takes a ``vtkImageData`` and probes its scalars at the specified points in space.
"""
- src = vtk.vtkProgrammableSource()
+ src = vtk.vtkProgrammableSource()
def readPoints():
output = src.GetPolyDataOutput()
points = vtk.vtkPoints()
@@ -1118,13 +1112,13 @@ def probePlane(img, origin=(0, 0, 0), normal=(1, 0, 0)):
return cutActor
-def imageOperation(image1, operation="+", image2=None):
+def imageOperation(image1, operation, image2=None):
"""
- Perform operations with ``vtkImageData`` objects.
+ Perform operations with ``vtkImageData`` objects.
`image2` can be a constant value.
- Possible operations are: ``+``, ``-``, ``/``, ``1/x``, ``sin``, ``cos``, ``exp``, ``log``,
+ Possible operations are: ``+``, ``-``, ``/``, ``1/x``, ``sin``, ``cos``, ``exp``, ``log``,
``abs``, ``**2``, ``sqrt``, ``min``, ``max``, ``atan``, ``atan2``, ``median``,
``mag``, ``dot``, ``gradient``, ``divergence``, ``laplacian``.
@@ -1389,7 +1383,7 @@ def thinPlateSpline(actor, sourcePts, targetPts, userFunctions=(None, None)):
"""
`Thin Plate Spline` transformations describe a nonlinear warp transform defined by a set
of source and target landmarks. Any point on the mesh close to a source landmark will
- be moved to a place close to the corresponding target landmark.
+ be moved to a place close to the corresponding target landmark.
The points in between are interpolated smoothly using Bookstein's Thin Plate Spline algorithm.
Transformation object is saved in ``actor.info['transform']``.
@@ -1514,60 +1508,83 @@ def meshQuality(actor, measure=6):
return qactor
-#def connectedPoints(point, actor, mode, radius, seeds, regions, vrange, normal):
-## https://vtk.org/doc/nightly/html/classvtkConnectedPointsFilter.html
-# # Extract all regions
-# cpf0 = vtk.vtkConnectedPointsFilter()
-# cpf0.SetInputData(actor.polydata())
-# cpf0.SetRadius(radius)
-# cpf0.Update()
-#
-# # Extract point seeded regions
-# cpf1 = vtk.vtkConnectedPointsFilter()
-# cpf1.SetInputData(actor.polydata())
-# cpf1.SetRadius(radius)
-# cpf1.SetExtractionModeToPointSeededRegions()
-# cpf1.AddSeed(0)
-# cpf1.AddSeed(2*100)
-# cpf1.Update()
-#
-# # Test largest region
-# cpf2 = vtk.vtkConnectedPointsFilter()
-# cpf2.SetInputData(actor.polydata())
-# cpf2.SetRadius(radius)
-# cpf2.SetExtractionModeToLargestRegion()
-# cpf2.Update()
-#
-# # Test specified regions
-# cpf3 = vtk.vtkConnectedPointsFilter()
-# cpf3.SetInputData(actor.polydata())
-# cpf3.SetRadius(radius)
-# cpf3.SetExtractionModeToSpecifiedRegions()
-# cpf3.AddSpecifiedRegion(1)
-# cpf3.AddSpecifiedRegion(3)
-# cpf3.Update()
-#
-# # Extract all regions with scalar connectivity
-# cpf4 = vtk.vtkConnectedPointsFilter()
-# cpf4.SetInputData(actor.polydata())
-# cpf4.SetRadius(radius);
-# cpf4.SetExtractionModeToLargestRegion()
-# cpf4.ScalarConnectivityOn()
-# cpf4.SetScalarRange(0, 0.99)
-# cpf4.Update()
-#
-# # Extract point seeded regions
-# cpf5 = vtk.vtkConnectedPointsFilter()
-# cpf5.SetInputData(actor.polydata())
-# cpf5.SetRadius(radius)
-# cpf5.SetExtractionModeToLargestRegion()
-# cpf5.ScalarConnectivityOn()
-# cpf5.SetScalarRange(0, 0.99)
-# cpf5.AlignedNormalsOn()
-# cpf5.SetNormalAngle(12.5)
-# cpf5.Update()
-#
-# return None
+def connectedPoints(actor, radius, mode=0, regions=(), vrange=(0,1), seeds=(), angle=0):
+ """
+ Extracts and/or segments points from a point cloud based on geometric distance measures
+ (e.g., proximity, normal alignments, etc.) and optional measures such as scalar range.
+ The default operation is to segment the points into "connected" regions where the connection
+ is determined by an appropriate distance measure. Each region is given a region id.
+
+ Optionally, the filter can output the largest connected region of points; a particular region
+ (via id specification); those regions that are seeded using a list of input point ids;
+ or the region of points closest to a specified position.
+
+ The key parameter of this filter is the radius defining a sphere around each point which defines
+ a local neighborhood: any other points in the local neighborhood are assumed connected to the point.
+ Note that the radius is defined in absolute terms.
+
+ Other parameters are used to further qualify what it means to be a neighboring point.
+ For example, scalar range and/or point normals can be used to further constrain the neighborhood.
+ Also the extraction mode defines how the filter operates.
+ By default, all regions are extracted but it is possible to extract particular regions;
+ the region closest to a seed point; seeded regions; or the largest region found while processing.
+ By default, all regions are extracted.
+
+ On output, all points are labeled with a region number.
+ However note that the number of input and output points may not be the same:
+ if not extracting all regions then the output size may be less than the input size.
+
+ :param float radius: radius variable specifying a local sphere used to define local point neighborhood
+ :param int mode:
+
+ - 0, Extract all regions
+ - 1, Extract point seeded regions
+ - 2, Extract largest region
+ - 3, Test specified regions
+ - 4, Extract all regions with scalar connectivity
+ - 5, Extract point seeded regions
+
+ :param list regions: a list of non-negative regions id to extract
+ :param list vrange: scalar range to use to extract points based on scalar connectivity
+ :param list seeds: a list of non-negative point seed ids
+ :param list angle: points are connected if the angle between their normals is
+ within this angle threshold (expressed in degrees).
+ """
+ # https://vtk.org/doc/nightly/html/classvtkConnectedPointsFilter.html
+ cpf = vtk.vtkConnectedPointsFilter()
+ cpf.SetInputData(actor.polydata())
+ cpf.SetRadius(radius)
+ if mode == 0: # Extract all regions
+ pass
+
+ elif mode == 1: # Extract point seeded regions
+ cpf.SetExtractionModeToPointSeededRegions()
+ for s in seeds:
+ cpf.AddSeed(s)
+
+ elif mode == 2: # Test largest region
+ cpf.SetExtractionModeToLargestRegion()
+
+ elif mode == 3: # Test specified regions
+ cpf.SetExtractionModeToSpecifiedRegions()
+ for r in regions:
+ cpf.AddSpecifiedRegion(r)
+
+ elif mode == 4: # Extract all regions with scalar connectivity
+ cpf.SetExtractionModeToLargestRegion()
+ cpf.ScalarConnectivityOn()
+ cpf.SetScalarRange(vrange[0], vrange[1])
+
+ elif mode == 5: # Extract point seeded regions
+ cpf.SetExtractionModeToLargestRegion()
+ cpf.ScalarConnectivityOn()
+ cpf.SetScalarRange(vrange[0], vrange[1])
+ cpf.AlignedNormalsOn()
+ cpf.SetNormalAngle(angle)
+
+ cpf.Update()
+
+ return Actor(cpf.GetOutput())
def splitByConnectivity(actor, maxdepth=100):
@@ -1715,7 +1732,7 @@ def convexHull(actor_or_list, alphaConstant=0):
def actor2ImageData(actor, spacing=(1, 1, 1)):
"""
Convert a mesh it into volume representation as ``vtkImageData``
- where the foreground (exterior) voxels are 1 and the background
+ where the foreground (exterior) voxels are 1 and the background
(interior) voxels are 0.
Internally the ``vtkPolyDataToImageStencil`` class is used.
@@ -1849,11 +1866,11 @@ def voronoi3D(nuclei, bbfactor=1, tol=None):
p = tuple(map(float, ls[i][1:-1].split(',')))
aid = sourcePoints.InsertNextPoint(p[0], p[1], p[2])
if tol:
- bp = np.array([p[0]-b[0], p[0]-b[1],
+ bp = np.array([p[0]-b[0], p[0]-b[1],
p[1]-b[2], p[1]-b[3],
p[2]-b[4], p[2]-b[5]])
bp = np.abs(bp) < tol
- if np.any(bp):
+ if np.any(bp):
ids.append(None)
else:
ids.append(aid)
@@ -1863,7 +1880,7 @@ def voronoi3D(nuclei, bbfactor=1, tol=None):
# fill polygon elements
if None in ids:
continue
-
+
faces = []
for j in range(n+3, len(ls)):
face = tuple(map(int, ls[j][1:-1].split(',')))
@@ -1889,4 +1906,66 @@ def voronoi3D(nuclei, bbfactor=1, tol=None):
return voro
+def interpolateToImageData(actor, kernel='shepard', radius=None,
+ bounds=None, nullValue=None,
+ dims=(20,20,20)):
+ """
+ Generate a voxel dataset (vtkImageData) by interpolating a scalar
+ which is only known on a scattered set of points or mesh.
+ Available interpolation kernels are: shepard, gaussian, voronoi, linear.
+
+ :param str kernel: interpolation kernel type [shepard]
+ :param float radius: radius of the local search
+ :param list bounds: bounding box of the output vtkImageData object
+ :param list dims: dimensions of the output vtkImageData object
+ :param float nullValue: value to be assigned to invalid points
+ """
+
+ output = actor.polydata()
+
+ # Create a probe volume
+ probe = vtk.vtkImageData()
+ probe.SetDimensions(dims)
+ if bounds is None:
+ bounds = output.GetBounds()
+ probe.SetOrigin(bounds[0],bounds[2],bounds[4])
+ probe.SetSpacing((bounds[1]-bounds[0])/(dims[0]-1),
+ (bounds[3]-bounds[2])/(dims[1]-1),
+ (bounds[5]-bounds[4])/(dims[2]-1))
+
+ if radius is None:
+ radius = min(bounds[1]-bounds[0], bounds[3]-bounds[2], bounds[5]-bounds[4])/3
+
+ locator = vtk.vtkPointLocator()
+ locator.SetDataSet(output)
+ locator.BuildLocator()
+
+ if kernel == 'shepard':
+ kern = vtk.vtkShepardKernel()
+ kern.SetPowerParameter(2)
+ kern.SetRadius(radius)
+ elif kernel == 'gaussian':
+ kern = vtk.vtkGaussianKernel()
+ kern.SetRadius(radius)
+ elif kernel == 'voronoi':
+ kern = vtk.vtkVoronoiKernel()
+ elif kernel == 'linear':
+ kern = vtk.vtkLinearKernel()
+ kern.SetRadius(radius)
+ else:
+ print('Error in interpolateToImageData, available kernels are:')
+ print(' [shepard, gaussian, voronoi, linear]')
+ exit()
+
+ interpolator = vtk.vtkPointInterpolator()
+ interpolator.SetInputData(probe)
+ interpolator.SetSourceData(output)
+ interpolator.SetKernel(kern)
+ interpolator.SetLocator(locator)
+ if nullValue is not None:
+ interpolator.SetNullValue(nullValue)
+ else:
+ interpolator.SetNullPointsStrategyToClosestPoint()
+ interpolator.Update()
+ return interpolator.GetOutput()
diff --git a/vtkplotter/colors.py b/vtkplotter/colors.py
index c92577ec..81d55f7e 100644
--- a/vtkplotter/colors.py
+++ b/vtkplotter/colors.py
@@ -45,7 +45,7 @@
}
except:
_mapscales = None
- pass # see below, this is dealt with in colorMap()
+ # see below, this is dealt with in colorMap()
#########################################################
@@ -177,7 +177,7 @@ def _isSequence(arg):
def getColor(rgb=None, hsv=None):
"""
- Convert a color to (r,g,b) format from many input formats.
+ Convert a color or list of colors to (r,g,b) format from many input formats.
:param bool hsv: if set to `True`, rgb is assumed as (hue, saturation, value).
@@ -193,6 +193,13 @@ def getColor(rgb=None, hsv=None):
.. hint:: |colorcubes| |colorcubes.py|_
"""
+ #recursion, return a list if input is list of colors:
+ if _isSequence(rgb) and len(rgb) > 3:
+ seqcol = []
+ for sc in rgb:
+ seqcol.append(getColor(sc))
+ return seqcol
+
if str(rgb).isdigit():
rgb = int(rgb)
@@ -208,7 +215,7 @@ def getColor(rgb=None, hsv=None):
if len(c) == 3:
return list(np.array(c) / 255.0) # RGB
else:
- return [c[0] / 255.0, c[1] / 255.0, c[2] / 255.0, c[3]] # RGBA
+ return (c[0] / 255.0, c[1] / 255.0, c[2] / 255.0, c[3]) # RGBA
elif isinstance(c, str): # is string
c = c.replace(",", " ").replace("/", " ").replace("alpha=", "")
@@ -220,7 +227,7 @@ def getColor(rgb=None, hsv=None):
else:
print("Unknow color nickname:", c)
print("Available abbreviations:", color_nicks)
- return [0.5, 0.5, 0.5]
+ return (0.5, 0.5, 0.5)
if c.lower() in colors.keys(): # matplotlib name color
c = colors[c.lower()]
@@ -236,8 +243,8 @@ def getColor(rgb=None, hsv=None):
rgbh = np.array(rgb255) / 255.0
if np.sum(rgbh) > 3:
print("Error in getColor(): Wrong hex color", c)
- return [0.5, 0.5, 0.5]
- return list(rgbh)
+ return (0.5, 0.5, 0.5)
+ return tuple(rgbh)
elif isinstance(c, int): # color number
if c >= 0:
@@ -251,8 +258,8 @@ def getColor(rgb=None, hsv=None):
else:
return colors2[int(-c) % 10]
- print("Unknown color:", c)
- return [0.5, 0.5, 0.5]
+ #print("Unknown color:", c)
+ return (0.5, 0.5, 0.5)
def getColorName(c):
@@ -306,6 +313,7 @@ def colorMap(value, name="jet", vmin=None, vmax=None):
from vtkplotter import colorMap
import matplotlib.cm as cm
print( colorMap(0.2, cm.flag, 0, 1) )
+
(1.0, 0.809016994374948, 0.6173258487801733)
"""
if not _mapscales:
@@ -351,7 +359,7 @@ def colorMap(value, name="jet", vmin=None, vmax=None):
def makePalette(color1, color2, N, hsv=True):
"""
- Generate N colors starting from `color1` to `color2`
+ Generate N colors starting from `color1` to `color2`
by linear interpolation HSV in or RGB spaces.
:param int N: number of output colors.
@@ -380,7 +388,7 @@ def makeLUTfromCTF(sclist, N=None):
Use a Color Transfer Function to generate colors in a vtk lookup table.
See `here `_.
- :param list sclist: a list in the form ``[(scalar1, [r,g,b]), (scalar2, 'blue'), ...]``.
+ :param list sclist: a list in the form ``[(scalar1, [r,g,b]), (scalar2, 'blue'), ...]``.
:return: the lookup table object ``vtkLookupTable``. This can be fed into ``colorMap``.
"""
ctf = vtk.vtkColorTransferFunction()
@@ -625,8 +633,10 @@ def printc(*strings, **keys):
printc('anything', c='red', bold=False, end='' )
printc('anything', 455.5, vtkObject, c='green')
printc(299792.48, c=4) # 4 is blue
+
+ .. hint:: |colorprint.py|_
- .. hint:: |colorprint| |colorprint.py|_
+ |colorprint|
"""
end = keys.pop("end", "\n")
@@ -761,7 +771,7 @@ def printHistogram(data, bins=10, height=10, logscale=False, minbin=0,
:param bool char: use boldface
:param str title: histogram title
- :Example:
+ :Example:
.. code-block:: python
from vtkplotter import printHistogram
@@ -772,7 +782,7 @@ def printHistogram(data, bins=10, height=10, logscale=False, minbin=0,
|printhisto|
"""
- # Adapted from http://pyinsci.blogspot.com/2009/10/ascii-histograms.html
+ # Adapted from http://pyinsci.blogspot.com/2009/10/ascii-histograms.html
if not horizontal: # better aspect ratio
bins *= 2
@@ -800,9 +810,9 @@ def printHistogram(data, bins=10, height=10, logscale=False, minbin=0,
from vtk.util.numpy_support import vtk_to_numpy
data = vtk_to_numpy(arr)
-
- h = np.histogram(data, bins=bins)
-
+
+ h = np.histogram(data, bins=bins)
+
if minbin:
hi = h[0][minbin:-1]
else:
@@ -812,7 +822,7 @@ def printHistogram(data, bins=10, height=10, logscale=False, minbin=0,
char = "*" # python2 hack
if char == u"\U00002589" and horizontal:
char = u"\U00002586"
-
+
entrs = "\t(entries=" + str(len(data)) + ")"
if logscale:
h0 = np.log10(hi+1)
@@ -831,7 +841,7 @@ def _v():
for l in reversed(range(1, height + 1)):
line = ""
if l == height:
- line = "%s " % maxh0
+ line = "%s " % maxh0
else:
line = " |" + " " * (len(str(maxh0))-3)
for c in bars:
diff --git a/vtkplotter/data/lshape.xml.gz b/vtkplotter/data/lshape.xml.gz
new file mode 100644
index 00000000..e280aa4e
Binary files /dev/null and b/vtkplotter/data/lshape.xml.gz differ
diff --git a/vtkplotter/docs.py b/vtkplotter/docs.py
index e072f227..5d752986 100644
--- a/vtkplotter/docs.py
+++ b/vtkplotter/docs.py
@@ -23,15 +23,6 @@
"""
from __future__ import division, print_function
-#from vtkplotter.settings import enableDolfin
-#if enableDolfin:
-# try:
-# import dolfin
-# except ModuleNotFoundError:
-# print("\nDolfin/Fenics package not found. Install it or set:")
-# print("vtkplotter.enableDolfin = False\nAbort.\n")
-# exit()
-
def onelinetip():
import vtk, sys
@@ -75,6 +66,8 @@ def tips():
msg += "| Middle-click to pan scene |\n"
msg += "| Right-click to zoom scene in or out |\n"
msg += "| Cntrl-click to rotate scene perpendicularly |\n"
+ msg += "|------ |\n"
+ msg += "|Check out documentation at: https://vtkplotter.embl.es |\n"
msg += "--------------------------------------------------------------\n"
colors.printc(msg, dim=1)
@@ -111,7 +104,6 @@ def tips():
.. |fillholes.py| replace:: fillholes.py
.. _fillholes.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/fillholes.py
- :width: 250 px
.. |quadratic_morphing.py| replace:: quadratic_morphing.py
.. _quadratic_morphing.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/quadratic_morphing.py
@@ -441,8 +433,8 @@ def tips():
.. |tannerhelland| replace:: tannerhelland
.. _tannerhelland: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code
-.. |colorprint.py| replace:: colorprint.py
-.. _colorprint.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/colorprint.py
+.. |colorprint.py| replace:: printc.py
+.. _colorprint.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/printc.py
.. |colorprint| image:: https://user-images.githubusercontent.com/32848391/50739010-2bfc2b80-11da-11e9-94de-011e50a86e61.jpg
:target: colorprint.py_
:alt: colorprint.py
@@ -634,6 +626,13 @@ def tips():
:target: glyphs.py_
:alt: glyphs.py
+.. |glyphs_arrow.py| replace:: glyphs_arrow.py
+.. _glyphs_arrow.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/glyphs_arrow.py
+.. |glyphs_arrow| image:: https://user-images.githubusercontent.com/32848391/55897850-a1a0da80-5bc1-11e9-81e0-004c8f396b43.jpg
+ :width: 250 px
+ :target: glyphs_arrow.py_
+ :alt: glyphs_arrow.py
+
.. |interpolateField.py| replace:: interpolateField.py
.. _interpolateField.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/advanced/interpolateField.py
.. |interpolateField| image:: https://user-images.githubusercontent.com/32848391/52416117-25b6e300-2ae9-11e9-8d86-575b97e543c0.png
@@ -688,18 +687,18 @@ def tips():
:alt: sliders3d.py
.. |ex01_showmesh.py| replace:: ex01_showmesh.py
-.. _ex01_showmesh.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex01_showmesh.py
+.. _ex01_showmesh.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex01_show-mesh.py
.. |ex01_showmesh| image:: https://user-images.githubusercontent.com/32848391/53026243-d2d31900-3462-11e9-9dde-518218c241b6.jpg
:width: 250 px
:target: ex01_showmesh.py_
:alt: ex01_showmesh.py
-.. |ex02_tetralize_mesh.py| replace:: ex02_tetralize_mesh.py
-.. _ex02_tetralize_mesh.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex02_tetralize_mesh.py
-.. |ex02_tetralize_mesh| image:: https://user-images.githubusercontent.com/32848391/53026244-d2d31900-3462-11e9-835a-1fa9d66d3dae.png
+.. |ex02_tetralize-mesh.py| replace:: ex02_tetralize-mesh.py
+.. _ex02_tetralize-mesh.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex02_tetralize-mesh.py
+.. |ex02_tetralize-mesh| image:: https://user-images.githubusercontent.com/32848391/53026244-d2d31900-3462-11e9-835a-1fa9d66d3dae.png
:width: 250 px
- :target: ex02_tetralize_mesh.py_
- :alt: ex02_tetralize_mesh.py
+ :target: ex02_tetralize-mesh.py_
+ :alt: ex02_tetralize-mesh.py
.. |ex06_elasticity1.py| replace:: ex06_elasticity1.py
.. _ex06_elasticity1.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex06_elasticity1.py
@@ -728,16 +727,13 @@ def tips():
.. |pmatrix| image:: https://user-images.githubusercontent.com/32848391/55098070-6da3c080-50bd-11e9-8f2b-be94a3f01831.png
:width: 250 px
-
-.. |stokes.py| replace:: stokes.py
-.. _stokes.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/stokes.py
-.. |stokes| image:: https://user-images.githubusercontent.com/32848391/55098209-aba0e480-50bd-11e9-8842-42d3f0b2d9c8.png
- :width: 250 px
- :target: stokes.py_
- :alt: stokes.py
.. |distance2mesh.py| replace:: distance2mesh.py
.. _distance2mesh.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/distance2mesh.py
+.. |distance2mesh| image:: https://user-images.githubusercontent.com/32848391/55965881-b5a71380-5c77-11e9-8680-5bddceab813a.png
+ :width: 250 px
+ :target: distance2mesh.py_
+ :alt: distance2mesh.py
.. |pendulum.py| replace:: pendulum.py
.. _pendulum.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/simulations/pendulum.py
@@ -754,9 +750,94 @@ def tips():
:alt: latex.py
.. |ft04_heat_gaussian.py| replace:: ft04_heat_gaussian.py
-.. _.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ft04_heat_gaussian.py
+.. _ft04_heat_gaussian.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ft04_heat_gaussian.py
.. |ft04_heat_gaussian| image:: https://user-images.githubusercontent.com/32848391/55578167-88a5ae80-5715-11e9-84ea-bdab54099887.gif
:width: 250 px
:target: ft04_heat_gaussian.py_
:alt: ft04_heat_gaussian.py
+
+.. |scalbar| image:: https://user-images.githubusercontent.com/32848391/55964528-2ac51980-5c75-11e9-9357-8c13d753a612.png
+ :width: 250 px
+
+.. |cutcube| image:: https://user-images.githubusercontent.com/32848391/55965516-08cc9680-5c77-11e9-8d23-720f6c088ea2.png
+ :width: 200 px
+
+.. |intline| image:: https://user-images.githubusercontent.com/32848391/55967065-eee08300-5c79-11e9-8933-265e1bab9f7e.png
+ :width: 350 px
+
+
+.. |turing_pattern.py| replace:: turing_pattern.py
+.. _turing_pattern.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/turing_pattern.py
+.. |turing_pattern| image:: https://user-images.githubusercontent.com/32848391/56056437-77cfeb00-5d5c-11e9-9887-828e5745d547.gif
+ :width: 250 px
+ :target: turing_pattern.py_
+ :alt: turing_pattern.py
+
+.. |demo_cahn-hilliard.py| replace:: demo_cahn-hilliard.py
+.. _demo_cahn-hilliard.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/demo_cahn-hilliard.py
+.. |demo_cahn-hilliard| image:: https://user-images.githubusercontent.com/32848391/56664730-edb34b00-66a8-11e9-9bf3-73431f2a98ac.gif
+ :width: 250 px
+ :target: demo_cahn-hilliard.py_
+ :alt: demo_cahn-hilliard.py
+
+
+.. |navier-stokes_lshape.py| replace:: navier-stokes_lshape.py
+.. _navier-stokes_lshape.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/navier-stokes_lshape.py
+.. |navier-stokes_lshape| image:: https://user-images.githubusercontent.com/32848391/56671156-6bc91f00-66b4-11e9-8c58-e6b71e2ad1d0.gif
+ :width: 250 px
+ :target: navier-stokes_lshape.py_
+ :alt: navier-stokes_lshape.py
+
+
+.. |mesh_map2cell.py| replace:: mesh_map2cell.py
+.. _mesh_map2cell.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/mesh_map2cell.py
+.. |mesh_map2cell| image:: https://user-images.githubusercontent.com/32848391/56600859-0153a880-65fa-11e9-88be-34fd96b18e9a.png
+ :width: 250 px
+ :target: mesh_map2cell.py_
+ :alt: mesh_map2cell.py
+
+
+.. |ex03_poisson.py| replace:: ex03_poisson.py
+.. _ex03_poisson.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ex03_poisson.py
+.. |ex03_poisson| image:: https://user-images.githubusercontent.com/32848391/54925524-bec18200-4f0e-11e9-9eab-29fd61ef3b8e.png
+ :width: 250 px
+ :target: ex03_poisson.py_
+ :alt: ex03_poisson.py
+
+.. |elastodynamics.py| replace:: elastodynamics.py
+.. _elastodynamics.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/elastodynamics.py
+.. |elastodynamics| image:: https://user-images.githubusercontent.com/32848391/54932788-bd4a8680-4f1b-11e9-9326-33645171a45e.gif
+ :width: 250 px
+ :target: elastodynamics.py_
+ :alt: elastodynamics.py
+
+.. |ft02_poisson_membrane.py| replace:: ft02_poisson_membrane.py
+.. _ft02_poisson_membrane.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/ft02_poisson_membrane.py
+.. |ft02_poisson_membrane| image:: https://user-images.githubusercontent.com/32848391/55499287-ed91d380-5645-11e9-8e9a-e31e2e3b1649.jpg
+ :width: 250 px
+ :target: ft02_poisson_membrane.py_
+ :alt: ft02_poisson_membrane.py
+
+
+.. |stokes.py| replace:: stokes.py
+.. _stokes.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/stokes.py
+.. |stokes| image:: https://user-images.githubusercontent.com/32848391/55098209-aba0e480-50bd-11e9-8842-42d3f0b2d9c8.png
+ :width: 250 px
+ :target: stokes.py_
+ :alt: stokes.py
+
+.. |demo_submesh.py| replace:: demo_submesh.py
+.. _demo_submesh.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/demo_submesh.py
+.. |demo_submesh| image:: https://user-images.githubusercontent.com/32848391/56675428-4e984e80-66bc-11e9-90b0-43dde7e4cc29.png
+ :width: 250 px
+ :target: demo_submesh.py_
+ :alt: demo_submesh.py
+
+.. |pi_estimate.py| replace:: pi_estimate.py
+.. _pi_estimate.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/dolfin/pi_estimate.py
+.. |pi_estimate| image:: https://user-images.githubusercontent.com/32848391/56675429-4e984e80-66bc-11e9-9217-a0652a8e74fe.png
+ :width: 250 px
+ :target: pi_estimate.py_
+ :alt: pi_estimate.py
+
"""
diff --git a/vtkplotter/dolfin.py b/vtkplotter/dolfin.py
index 9e404d68..88d02ebc 100644
--- a/vtkplotter/dolfin.py
+++ b/vtkplotter/dolfin.py
@@ -2,6 +2,7 @@
#
from __future__ import division, print_function
+import vtk
from vtk.util.numpy_support import numpy_to_vtk
import numpy as np
@@ -9,7 +10,6 @@
import vtkplotter.utils as utils
import vtkplotter.docs as docs
-import vtkplotter.colors as colors
from vtkplotter.colors import printc, printHistogram
import vtkplotter.settings as settings
@@ -29,7 +29,7 @@
__doc__ = (
"""
-FEniCS/Dolfin support submodule.
+`FEniCS/Dolfin https://fenicsproject.org`_ support submodule.
Install with commands (e.g. in Anaconda):
@@ -51,8 +51,29 @@
.. image:: https://user-images.githubusercontent.com/32848391/53026243-d2d31900-3462-11e9-9dde-518218c241b6.jpg
-Find many more examples in
+Find many more examples in
`vtkplotter/examples/dolfin `_
+
+
+Image Gallery
+=============
+
+*(click on the thumbnail to get to the script)*
+
++--------------------------------+--------------------------------+
+| |ex03_poisson| | |ex02_tetralize-mesh| |
++--------------------------------+--------------------------------+
+| |demo_submesh| | |pi_estimate| |
++--------------------------------+--------------------------------+
+| |ex06_elasticity1| | |ex06_elasticity2| |
++--------------------------------+--------------------------------+
+| |ft04_heat_gaussian| | |demo_cahn-hilliard| |
++--------------------------------+--------------------------------+
+| |navier-stokes_lshape| | |turing_pattern| |
++--------------------------------+--------------------------------+
+| |elastodynamics| | |ft02_poisson_membrane| |
++--------------------------------+--------------------------------+
+
"""
+ docs._defs
)
@@ -102,8 +123,8 @@ def _inputsort(obj):
V = dolfin.FunctionSpace(mesh, "CG", 1)
elif r == 1: # maybe wrong:
V = dolfin.VectorFunctionSpace(mesh, "CG", 1, dim=r)
- else: # very wrong:
- V = dolfin.TensorFunctionSpace(mesh, "CG", 1, shape=(r,r))
+# else: # very wrong:
+# V = dolfin.TensorFunctionSpace(mesh, "CG", 1, shape=(r,r))
u = dolfin.Function(V)
v2d = dolfin.vertex_to_dof_map(V)
u.vector()[v2d] = ob.array()
@@ -113,7 +134,7 @@ def _inputsort(obj):
return None
# tdim = mesh.topology().dim()
-# d = ob.dim()
+# d = ob.dim()
# if tdim == 2 and d == 2:
# import matplotlib.tri as tri
# xy = mesh.coordinates()
@@ -148,7 +169,7 @@ def _inputsort(obj):
def plot(*inputobj, **options):
"""
- Plot the object(s) provided.
+ Plot the object(s) provided.
Input can be: ``vtkActor``, ``vtkVolume``, ``dolfin.Mesh``, ``dolfin.MeshFunction*``,
``dolfin.Expression`` or ``dolfin.Function``.
@@ -164,20 +185,23 @@ def plot(*inputobj, **options):
- `arrows`, mesh displacements are plotted as scaled arrows.
- `lines`, mesh displacements are plotted as scaled lines.
- `tensors`, to be implemented
-
+
+ :param bool add: add the input objects without clearing the previous ones
+ :param float density: show only a subset of lines or arrows [0-1]
:param bool wire[frame]: visualize mesh as wireframe [False]
:param c[olor]: set mesh color [None]
:param float alpha: set object's transparency [1]
:param float lw: line width of the mesh (set to zero to hide mesh) [0.5]
:param float ps: set point size of mesh vertices [None]
+ :param float z: add a constant to z-coordinate (useful to show 2D slices as function of time)
:param str legend: add a legend to the top-right of window [None]
:param bool scalarbar: add a scalarbar to the window ['vertical']
:param float vmin: set the minimum for the range of the scalar [None]
:param float vmax: set the maximum for the range of the scalar [None]
:param float scale: add a scaling factor to arrows and lines sizes [1]
:param str cmap: choose a color map for scalars
- :param int bands: group colors in `n` bands
- :param shading: mesh shading ['flat', 'phong', 'gouraud']
+ :param int bands: group colors in `n` bands
+ :param str shading: mesh shading ['flat', 'phong', 'gouraud']
:param str text: add a gray text comment to the top-left of the window [None]
:param bool newPlotter: spawn a new instance of Plotter class, pops up a new window
@@ -216,7 +240,7 @@ def plot(*inputobj, **options):
:param bool sharecam: if False each renderer will have an independent vtkCamera
:param bool interactive: if True will stop after show() to allow interaction w/ window
:param bool depthpeeling: depth-peel volumes along with the translucent geometry
- :param bool offscreen: if True will not show the rendering window
+ :param bool offscreen: if True will not show the rendering window
:param float zoom: camera zooming factor
:param viewup: camera view-up direction ['x','y','z', or a vector direction]
@@ -228,7 +252,7 @@ def plot(*inputobj, **options):
.. hint:: |ex01_showmesh| |ex01_showmesh.py|_
- |ex02_tetralize_mesh| |ex02_tetralize_mesh.py|_
+ |ex02_tetralize-mesh| |ex02_tetralize-mesh.py|_
|ex06_elasticity1| |ex06_elasticity1.py|_
@@ -238,12 +262,16 @@ def plot(*inputobj, **options):
if len(inputobj) == 0:
if settings.plotter_instance:
settings.plotter_instance.interactor.Start()
- return
+ return settings.plotter_instance
mesh, u = _inputsort(inputobj)
mode = options.pop("mode", 'mesh')
+ ttime = options.pop("z", None)
+ #density = options.pop("density", None) #todo
+ add = options.pop("add", False)
+
wire = options.pop("wire", False)
wireframe = options.pop("wireframe", None)
if wireframe is not None:
@@ -257,19 +285,23 @@ def plot(*inputobj, **options):
alpha = options.pop("alpha", 1)
lw = options.pop("lw", 0.5)
ps = options.pop("ps", None)
- legend = options.pop("legend", None)
+ legend = options.pop("legend", None)
scbar = options.pop("scalarbar", 'v')
vmin = options.pop("vmin", None)
vmax = options.pop("vmax", None)
- cmap = options.pop("cmap", None)
- bands = options.pop("bands", None)
+ cmap = options.pop("cmap", None)
+ bands = options.pop("bands", None)
scale = options.pop("scale", 1)
shading = options.pop("shading", None)
text = options.pop("text", None)
style = options.pop("style", 'vtk')
+
+ settings.xtitle = options.pop("xtitle", 'x')
+ settings.ytitle = options.pop("ytitle", 'y')
+ settings.ztitle = options.pop("ztitle", 'z')
# change some default to emulate matplotlib behaviour
- options['verbose'] = False # dont disturb
+ options['verbose'] = False # dont disturb
if style == 0 or style == 'vtk':
font = 'courier'
axes = options.pop('axes', None)
@@ -304,7 +336,7 @@ def plot(*inputobj, **options):
cmap = 'coolwarm'
elif style == 3 or style == 'meshlab':
font = 'courier'
- bg = options.pop('bg', None)
+ bg = options.pop('bg', None)
if bg is None:
options['bg'] = (8, 8, 16)
options['bg2'] = (117, 117, 234)
@@ -324,19 +356,24 @@ def plot(*inputobj, **options):
options['bg'] = (217, 255, 238)
else:
options['bg'] = bg
- axes = options.pop('axes', None)
+ axes = options.pop('axes', None)
if axes is None:
options['axes'] = 8
else:
options['axes'] = axes # put back
if cmap is None:
- cmap = 'gray'
+ cmap = 'binary'
#################################################################
actors = []
+ if add and settings.plotter_instance:
+ actors = settings.plotter_instance.actors
+
if 'mesh' in mode or 'color' in mode:
actor = MeshActor(u, mesh, wire=wire)
+ if ttime:
+ actor.z(ttime)
if legend:
actor.legend(legend)
if c:
@@ -363,16 +400,15 @@ def plot(*inputobj, **options):
delta = [u(p) for p in mesh.coordinates()]
#delta = u.compute_vertex_values(mesh) # needs reshape
if u.value_rank() > 0: # wiil show the size of the vector
- actor.pointColors(utils.mag(delta),
+ actor.pointColors(utils.mag(delta),
cmap=cmap, bands=bands, vmin=vmin, vmax=vmax)
else:
actor.pointColors(delta, cmap=cmap, bands=bands, vmin=vmin, vmax=vmax)
- if scbar:
- if c is None:
- if 'h' in scbar:
- actor.addScalarBar(horizontal=True, vmin=vmin, vmax=vmax)
- else:
- actor.addScalarBar(horizontal=False, vmin=vmin, vmax=vmax)
+ if scbar and c is None:
+ if 'h' in scbar:
+ actor.addScalarBar(horizontal=True, vmin=vmin, vmax=vmax)
+ else:
+ actor.addScalarBar(horizontal=False, vmin=vmin, vmax=vmax)
if 'warp' in mode or 'displace' in mode:
if delta is None:
@@ -405,7 +441,7 @@ def plot(*inputobj, **options):
#################################################################
if 'tensor' in mode:
- pass
+ pass #todo
#################################################################
@@ -413,25 +449,25 @@ def plot(*inputobj, **options):
inputtype = str(type(ob))
if 'vtk' in inputtype:
actors.append(ob)
-
- if text:
- bgc = (0.6, 0.6, 0.6)
- if 'bg' in options.keys():
- bgc = colors.getColor(options['bg'])
- if sum(bgc) < 1.5:
- bgc = 'w'#(0.9, 0.9, 0.9)
- else:
- bgc = (0.1, 0.1, 0.1)
- actors.append(Text(text, c=bgc, font=font))
+
+ if text:
+ textact = Text(text, font=font)
+ actors.append(textact)
- if 'at' in options.keys() and not 'interactive' in options.keys():
+ if 'at' in options.keys() and 'interactive' not in options.keys():
if settings.plotter_instance:
N = settings.plotter_instance.shape[0]*settings.plotter_instance.shape[1]
if options['at'] == N-1:
options['interactive'] = True
-
- vp = show(actors, **options)
- return vp
+
+ if settings.plotter_instance:
+ for a2 in settings.collectable_actors:
+ if isinstance(a2, vtk.vtkCornerAnnotation):
+ if 0 in a2.renderedAt: # remove old message
+ settings.plotter_instance.removeActor(a2)
+ break
+
+ return show(actors, **options)
###################################################################################
@@ -502,7 +538,7 @@ def MeshPoints(*inputobj, **options):
plist = np.insert(plist, 2, 0, axis=1) # make it 3d
if len(plist[0]) == 1: # coords are 1d.. not good..
plist = np.insert(plist, 1, 0, axis=1) # make it 3d
- plist = np.insert(plist, 2, 0, axis=1)
+ plist = np.insert(plist, 2, 0, axis=1)
actor = shapes.Points(plist, r=r, c=c, alpha=alpha)
@@ -524,10 +560,10 @@ def MeshLines(*inputobj, **options):
Build the line segments between two lists of points `startPoints` and `endPoints`.
`startPoints` can be also passed in the form ``[[point1, point2], ...]``.
- A dolfin ``Mesh`` that was deformed/modified by a function can be
+ A dolfin ``Mesh`` that was deformed/modified by a function can be
passed together as inputs.
- :param float scale: apply a rescaling factor to the length
+ :param float scale: apply a rescaling factor to the length
"""
scale = options.pop("scale", 1)
lw = options.pop("lw", 1)
@@ -561,7 +597,7 @@ def MeshArrows(*inputobj, **options):
Build arrows representing displacements.
:param float s: cross-section size of the arrow
- :param float rescale: apply a rescaling factor to the length
+ :param float rescale: apply a rescaling factor to the length
"""
s = options.pop("s", None)
scale = options.pop("scale", 1)
@@ -588,17 +624,22 @@ def MeshArrows(*inputobj, **options):
actor.u = u
actor.u_values = u_values
return actor
-
-def MeshTensors(*inputobj, **options):
- """Not yet implemented."""
+
+#def MeshTensors(*inputobj, **options):
+# """Not yet implemented."""
# c = options.pop("c", "gray")
# alpha = options.pop("alpha", 1)
# mesh, u = _inputsort(inputobj)
- return
+# return
+
+
+
+
+
+
-# -*- coding: utf-8 -*-
# Copyright (C) 2008-2012 Joachim B. Haga and Fredrik Valdmanis
#
# This file is part of DOLFIN.
diff --git a/vtkplotter/plotter.py b/vtkplotter/plotter.py
index e6c2371e..e16e3de9 100644
--- a/vtkplotter/plotter.py
+++ b/vtkplotter/plotter.py
@@ -116,8 +116,8 @@ def show(*actors, **options
elif len(actors) == 1:
actors = actors[0]
else:
- actors = utils.flatten(actors)
-
+ actors = utils.flatten(actors)
+
if settings.plotter_instance and newPlotter == False:
vp = settings.plotter_instance
else:
@@ -136,12 +136,6 @@ def show(*actors, **options
colors.printc(' you may need to specify e.g. at=0', c=1)
exit()
at = range(len(actors))
-
-# if settings.plotter_instance:
-# prevcam = settings.plotter_instance.camera
-# prevsharecam = settings.plotter_instance.sharecam
-# else:
-# prevcam = False
vp = Plotter(
shape=shape,
@@ -159,10 +153,6 @@ def show(*actors, **options
interactive=interactive,
offscreen=offscreen,
)
-
-# if prevcam:
-# vp.camera = prevcam
-
if utils.isSequence(at):
for i, a in enumerate(actors):
@@ -185,6 +175,7 @@ def show(*actors, **options
actors,
at=at,
zoom=zoom,
+ resetcam=resetcam,
viewup=viewup,
azimuth=azimuth,
elevation=elevation,
@@ -257,15 +248,15 @@ class Plotter:
:param list shape: shape of the grid of renderers in format (rows, columns).
Ignored if N is specified.
:param int N: number of desired renderers arranged in a grid automatically.
- :param list pos: (x,y) position in pixels of top-left corneer of the rendering window
+ :param list pos: (x,y) position in pixels of top-left corneer of the rendering window
on the screen
:param size: size of the rendering window. If 'auto', guess it based on screensize.
- :param screensize: physical size of the monitor screen
+ :param screensize: physical size of the monitor screen
:param bg: background color or specify jpg image file name with path
:param bg2: background color of a gradient towards the top
- :param int axes:
+ :param int axes:
- - 0, no axes,
+ - 0, no axes
- 1, draw three gray grid walls
- 2, show cartesian axes from (0,0,0)
- 3, show positive range of cartesian axes from (0,0,0)
@@ -273,19 +264,19 @@ class Plotter:
- 5, show a cube at bottom left
- 6, mark the corners of the bounding box
- 7, draw a simple ruler at the bottom of the window
- - 8, show the ``vtkCubeAxesActor`` object,
+ - 8, show the ``vtkCubeAxesActor`` object
- 9, show the bounding box outLine,
- 10, show three circles representing the maximum bounding box.
:param bool infinity: if True fugue point is set at infinity (no perspective effects)
:param bool sharecam: if False each renderer will have an independent vtkCamera
:param bool interactive: if True will stop after show() to allow interaction w/ window
- :param bool offscreen: if True will not show the rendering window
+ :param bool offscreen: if True will not show the rendering window
:param bool depthpeeling: depth-peel volumes along with the translucent geometry
|multiwindows|
"""
-
+
def __init__(
self,
shape=(1, 1),
@@ -328,9 +319,6 @@ def __init__(
self.interactive = interactive # allows to interact with renderer
self.axes = axes # show axes type nr.
self.title = title # window title
- self.xtitle = "x" # x axis label and units
- self.ytitle = "y" # y axis label and units
- self.ztitle = "z" # z axis label and units
self.sharecam = sharecam # share the same camera if multiple renderers
self.infinity = infinity # ParallelProjection On or Off
self._legend = [] # list of legend entries for actors
@@ -362,6 +350,10 @@ def __init__(
self.mouseLeftClickFunction = None
self.mouseMiddleClickFunction = None
self.mouseRightClickFunction = None
+
+ self.xtitle = settings.xtitle # x axis label and units
+ self.ytitle = settings.ytitle # y axis label and units
+ self.ztitle = settings.ztitle # z axis label and units
# sort out screen size
self.window = vtk.vtkRenderWindow()
@@ -532,7 +524,7 @@ def load(
threshold=None,
connectivity=False,
):
- """
+ """
Returns a ``vtkActor`` from reading a file, directory or ``vtkPolyData``.
:param c: color in RGB format, hex, symbol or name
@@ -555,6 +547,37 @@ def load(
return acts
+ def getVolumes(self, obj=None, renderer=None):
+ """
+ Return the list of the rendered Volumes.
+ """
+ if renderer is None:
+ renderer = self.renderer
+ elif isinstance(renderer, int):
+ renderer = self.renderers.index(renderer)
+ else:
+ return []
+
+ if obj is None or isinstance(obj, int):
+ if obj is None:
+ acs = renderer.GetVolumes()
+ elif obj >= len(self.renderers):
+ colors.printc("~timesError in getVolumes: non existing renderer", obj, c=1)
+ return []
+ else:
+ acs = self.renderers[obj].GetVolumes()
+ vols = []
+ acs.InitTraversal()
+ for i in range(acs.GetNumberOfItems()):
+ a = acs.GetNextItem()
+ if a.GetPickable():
+ r = self.renderers.index(renderer)
+ if a == self.axes_exist[r]:
+ continue
+ vols.append(a)
+ return vols
+
+
def getActors(self, obj=None, renderer=None):
"""
Return an actors list.
@@ -562,7 +585,7 @@ def getActors(self, obj=None, renderer=None):
If ``obj`` is:
``None``, return actors of current renderer
- ``int``, return actors in given renderer number
+ ``int``, return actors in given renderer number
``vtkAssembly`` return the contained actors
@@ -574,8 +597,7 @@ def getActors(self, obj=None, renderer=None):
renderer = self.renderer
elif isinstance(renderer, int):
renderer = self.renderers.index(renderer)
-
- if not renderer:
+ else:
return []
if obj is None or isinstance(obj, int):
@@ -611,7 +633,7 @@ def getActors(self, obj=None, renderer=None):
elif isinstance(obj, str): # search the actor by the legend name
actors = []
for a in self.actors:
- if hasattr(a, "_legend") and obj in a._legend and a.GetPickable():
+ if hasattr(a, "_legend") and obj in a._legend:
actors.append(a)
return actors
@@ -736,7 +758,7 @@ def addScalarBar3D(
nlabels=9,
ncols=256,
cmap=None,
- c="k",
+ c=None,
alpha=1,
):
"""Draw a 3D scalar bar.
@@ -747,7 +769,7 @@ def addScalarBar3D(
- a ``vtkActor`` already containing a set of scalars associated to vertices or cells,
- if ``None`` the last actor in the list of actors will be used.
- .. hint:: |mesh_coloring| |mesh_coloring.py|_
+ .. hint:: |scalbar| |mesh_coloring.py|_
"""
return addons.addScalarBar3D(obj, at, pos, normal, sx, sy, nlabels, ncols, cmap, c, alpha)
@@ -809,7 +831,7 @@ def addButton(
states=("On", "Off"),
c=("w", "w"),
bc=("dg", "dr"),
- pos=[20, 40],
+ pos=(20, 40),
size=24,
font="arial",
bold=False,
@@ -898,11 +920,11 @@ def show(
"""
Render a list of actors.
- Allowed input objects are: ``filename``, ``vtkPolyData``, ``vtkActor``,
+ Allowed input objects are: ``filename``, ``vtkPolyData``, ``vtkActor``,
``vtkActor2D``, ``vtkImageActor``, ``vtkAssembly`` or ``vtkVolume``.
If filename is given, its type is guessed based on its extension.
- Supported formats are:
+ Supported formats are:
`vtu, vts, vtp, ply, obj, stl, 3ds, xml, neutral, gmsh, pcd, xyz, txt, byu,
tif, slc, vti, mhd, png, jpg`.
@@ -927,7 +949,7 @@ def show(
:param float azimuth/elevation/roll: move camera accordingly
:param str viewup: either ['x', 'y', 'z'] or a vector to set vertical direction
:param bool resetcam: re-adjust camera position to fit objects
- :param bool interactive: pause and interact with window (True)
+ :param bool interactive: pause and interact with window (True)
or continue execution (False)
:param float rate: maximum rate of `show()` in Hertz
:param int interactorStyle: set the type of interaction
@@ -977,7 +999,12 @@ def scan(wannabeacts):
if a.trail and not a.trail in self.actors:
scannedacts.append(a.trail)
elif isinstance(a, vtk.vtkActor2D):
- scannedacts.append(a)
+ if isinstance(a, vtk.vtkCornerAnnotation):
+ for a2 in settings.collectable_actors:
+ if isinstance(a2, vtk.vtkCornerAnnotation):
+ if at in a2.renderedAt: # remove old message
+ self.removeActor(a2)
+ scannedacts.append(a)
elif isinstance(a, vtk.vtkImageActor):
scannedacts.append(a)
elif isinstance(a, vtk.vtkVolume):
@@ -1091,18 +1118,28 @@ def scan(wannabeacts):
self.renderer.AddVolume(ia)
else:
self.renderer.AddActor(ia)
+ if hasattr(ia, 'renderedAt'):
+ ia.renderedAt.add(at)
else:
colors.printc("~lightning Warning: Invalid actor in actors list, skip.", c=5)
+
# remove the ones that are not in actors2show
for ia in self.getActors(at):
if ia not in actors2show:
self.renderer.RemoveActor(ia)
-
+ if hasattr(ia, 'renderedAt'):
+ ia.renderedAt.discard(at)
+
+ for c in self.scalarbars:
+ self.renderer.RemoveActor(c)
+ if hasattr(c, 'renderedAt'):
+ c.renderedAt.discard(at)
+
if self.axes is not None:
addons.addAxes()
-
+
addons.addLegend()
-
+
if self.showFrame and len(self.renderers) > 1:
addons.addFrame()
@@ -1153,12 +1190,12 @@ def scan(wannabeacts):
):
if len(a.scalarbar) == 5: # addScalarBar
s1, s2, s3, s4, s5 = a.scalarbar
- sb = self.addScalarBar(a, s1, s2, s3, s4, s5)
+ sb = addons.addScalarBar(a, s1, s2, s3, s4, s5)
scbflag = True
a.scalarbar = sb # save scalarbar actor
elif len(a.scalarbar) == 10: # addScalarBar3D
s0, s1, s2, s3, s4, s5, s6, s7, s8 = a.scalarbar
- sb = self.addScalarBar3D(a, at, s0, s1, s2, s3, s4, s5, s6, s7, s8)
+ sb = addons.addScalarBar3D(a, at, s0, s1, s2, s3, s4, s5, s6, s7, s8)
scbflag = True
a.scalarbar = sb # save scalarbar actor
if scbflag:
@@ -1218,6 +1255,9 @@ def removeActor(self, a):
return
if self.renderer:
self.renderer.RemoveActor(a)
+ if hasattr(a, 'renderedAt'):
+ ir = self.renderers.index(self.renderer)
+ a.renderedAt.discard(ir)
if a in self.actors:
i = self.actors.index(a)
del self.actors[i]
@@ -1230,10 +1270,14 @@ def clear(self, actors=()):
for a in actors:
self.removeActor(a)
else:
- settings.collectable_actors = []
+ for a in settings.collectable_actors:
+ self.removeActor(a)
+ settings.collectable_actors = []
self.actors = []
for a in self.getActors():
self.renderer.RemoveActor(a)
+ for a in self.getVolumes():
+ self.renderer.RemoveVolume(a)
for s in self.sliders:
s.EnabledOff()
for b in self.buttons:
@@ -1242,3 +1286,7 @@ def clear(self, actors=()):
w.EnabledOff()
for c in self.scalarbars:
self.renderer.RemoveActor(c)
+
+
+
+
diff --git a/vtkplotter/settings.py b/vtkplotter/settings.py
index 52670ece..9467c05f 100644
--- a/vtkplotter/settings.py
+++ b/vtkplotter/settings.py
@@ -24,6 +24,9 @@
# allow to interact with scene during interactor.Start() execution
allowInteraction = True
+# usetex, matplotlib latex compiler
+usetex = False
+
# Qt embedding
usingQt = False
@@ -34,6 +37,12 @@
# http://math.lbl.gov/voro++
voro_path = '/usr/local/bin'
+# axes titles
+xtitle = 'x'
+ytitle = 'y'
+ztitle = 'z'
+
+
#####################
_cdir = os.path.dirname(__file__)
@@ -44,6 +53,9 @@
datadir = _cdir + "/data/"
+#####################
+collectable_actors = []
+
#####################
def _init():
diff --git a/vtkplotter/shapes.py b/vtkplotter/shapes.py
index f81e1792..85565753 100644
--- a/vtkplotter/shapes.py
+++ b/vtkplotter/shapes.py
@@ -146,19 +146,42 @@ def _colorPoints(plist, cols, r, alpha):
return actor
-def Glyph(actor, glyphObj, orientationArray="", scaleByVectorSize=False, c="gold", alpha=1):
+def Glyph(actor, glyphObj, orientationArray="",
+ scaleByVectorSize=False, c=None, alpha=1):
"""
At each vertex of a mesh, another mesh - a `'glyph'` - is shown with
various orientation options and coloring.
- :param orientationArray: list of vectors, ``vtkAbstractArray``
- or the name of an already existing points array.
+ Color can be specfied as a colormap which maps the size of the orientation
+ vectors in `orientationArray`.
+
+ :param orientationArray: list of vectors, ``vtkAbstractArray``
+ or the name of an already existing points array.
:type orientationArray: list, str, vtkAbstractArray
:param bool scaleByVectorSize: glyph mesh is scaled by the size of
the vectors.
.. hint:: |glyphs| |glyphs.py|_
+
+ |glyphs_arrow| |glyphs_arrow.py|_
"""
+ cmap = None
+ # user passing a color map to map orientationArray sizes
+ if c in list(colors._mapscales.keys()):
+ cmap = c
+ c = None
+
+ # user is passing an array of point colors
+ if utils.isSequence(c) and len(c) > 3:
+ ucols = vtk.vtkUnsignedCharArray()
+ ucols.SetNumberOfComponents(3)
+ ucols.SetName("glyphRGB")
+ for col in c:
+ cl = colors.getColor(col)
+ ucols.InsertNextTuple3(cl[0]*255, cl[1]*255, cl[2]*255)
+ actor.polydata().GetPointData().SetScalars(ucols)
+ c = None
+
if isinstance(glyphObj, Actor):
glyphObj = glyphObj.clean().polydata()
@@ -186,26 +209,33 @@ def Glyph(actor, glyphObj, orientationArray="", scaleByVectorSize=False, c="gold
elif utils.isSequence(orientationArray): # passing a list
actor.addPointVectors(orientationArray, "glyph_vectors")
gly.SetInputArrayToProcess(0, 0, 0, 0, "glyph_vectors")
- gly.SetVectorModeToUseVector()
else: # passing a name
gly.SetInputArrayToProcess(0, 0, 0, 0, orientationArray)
gly.SetVectorModeToUseVector()
+ if cmap:
+ gly.SetColorModeToColorByVector ()
+ else:
+ gly.SetColorModeToColorByScalar ()
- if utils.isSequence(c) and len(c) != 3:
- ucols = vtk.vtkUnsignedCharArray()
- ucols.SetNumberOfComponents(3)
- ucols.SetName("glyphRGB")
- for col in c:
- cl = colors.getColor(col)
- ucols.InsertNextTuple3(cl[0]*255, cl[1]*255, cl[2]*255)
- actor.polydata().GetPointData().SetScalars(ucols)
- gly.SetScaleModeToDataScalingOff()
-
gly.Update()
pd = gly.GetOutput()
actor = Actor(pd, c, alpha)
+
+ if cmap:
+ lut = vtk.vtkLookupTable()
+ lut.SetNumberOfTableValues(512)
+ lut.Build()
+ for i in range(512):
+ r, g, b = colors.colorMap(i, cmap, 0, 512)
+ lut.SetTableValue(i, r, g, b, 1)
+ actor.mapper.SetLookupTable(lut)
+ actor.mapper.ScalarVisibilityOn()
+ actor.mapper.SetScalarModeToUsePointData()
+ rng = pd.GetPointData().GetScalars().GetRange()
+ actor.mapper.SetScalarRange(rng[0], rng[1])
+
actor.GetProperty().SetInterpolationToFlat()
settings.collectable_actors.append(actor)
return actor
@@ -235,8 +265,7 @@ def Line(p0, p1=None, lw=1, c="r", alpha=1, dotted=False):
ppoints = vtk.vtkPoints() # Generate the polyline
dim = len((p0[0]))
if dim == 2:
- for i in range(len(p0)):
- p = p0[i]
+ for i, p in enumerate(p0):
ppoints.InsertPoint(i, p[0], p[1], 0)
else:
ppoints.SetData(numpy_to_vtk(p0, deep=True))
@@ -448,7 +477,7 @@ def FlatArrow(line1, line2, c="m", alpha=1, tipSize=1, tipWidth=1):
v = (sm1-sm2)/3*tipWidth
p1 = sm1+v
- p2 = sm2-v
+ p2 = sm2-v
pm1 = (sm1+sm2)/2
pm2 = (np.array(line1[-2])+np.array(line2[-2]))/2
pm12 = pm1-pm2
@@ -521,52 +550,39 @@ def Arrows(startPoints, endPoints=None, s=None, scale=1, c="r", alpha=1, res=12)
Build arrows between two lists of points `startPoints` and `endPoints`.
`startPoints` can be also passed in the form ``[[point1, point2], ...]``.
- A dolfin ``Mesh`` that was deformed/modified by a function can be
- passed together as inputs.
+ Color can be specfied as a colormap which maps the size of the arrows.
- :param float s: cross-section size of the arrow
- :param float scale: apply a rescaling factor to the length
- """
+ :param float s: fix aspect-ratio of the arrow and scale its cross section
+ :param float scale: apply a rescaling factor to the length
+ :param c: color or array of colors
+ :param str cmap: color arrows by size using this color map
+ :param float alpha: set transparency
+ :param int res: set arrow resolution
- if endPoints is not None:
- startPoints = list(zip(startPoints, endPoints))
-
- polyapp = vtk.vtkAppendPolyData()
- for twopts in startPoints:
- startPoint, endPoint = twopts
- axis = np.array(endPoint) - np.array(startPoint)
- length = np.linalg.norm(axis)
- if length:
- axis /= length
- theta = np.arccos(axis[2])
- phi = np.arctan2(axis[1], axis[0])
- arr = vtk.vtkArrowSource()
- arr.SetShaftResolution(res)
- arr.SetTipResolution(res)
- if s:
- sz = 0.02
- arr.SetTipRadius(sz)
- arr.SetShaftRadius(sz / 1.75)
- arr.SetTipLength(sz * 15)
- t = vtk.vtkTransform()
- t.Translate(startPoint)
- t.RotateZ(phi * 57.3)
- t.RotateY(theta * 57.3)
- t.RotateY(-90) # put it along Z
- if s:
- sz = 800.0 * s
- t.Scale(length*scale, sz*scale, sz*scale)
- else:
- t.Scale(length*scale, length*scale, length*scale)
- tf = vtk.vtkTransformPolyDataFilter()
- tf.SetInputConnection(arr.GetOutputPort())
- tf.SetTransform(t)
- polyapp.AddInputConnection(tf.GetOutputPort())
- polyapp.Update()
-
- actor = Actor(polyapp.GetOutput(), c, alpha)
- settings.collectable_actors.append(actor)
- return actor
+ .. hint:: |glyphs_arrow| |glyphs_arrow.py|_
+ """
+ startPoints = np.array(startPoints)
+ if endPoints is None:
+ strt = startPoints[:,0]
+ endPoints = startPoints[:,1]
+ startPoints = strt
+
+ arr = vtk.vtkArrowSource()
+ arr.SetShaftResolution(res)
+ arr.SetTipResolution(res)
+ if s:
+ sz = 0.02 * s
+ arr.SetTipRadius(sz*2)
+ arr.SetShaftRadius(sz)
+ arr.SetTipLength(sz * 10)
+ arr.Update()
+ pts = Points(startPoints)
+ orients = (endPoints - startPoints) * scale
+ arrg = Glyph(pts, arr.GetOutput(),
+ orientationArray=orients, scaleByVectorSize=True,
+ c=c, alpha=alpha)
+ settings.collectable_actors.append(arrg)
+ return arrg
def Polygon(pos=(0, 0, 0), normal=(0, 0, 1), nsides=6, r=1, c="coral",
@@ -576,7 +592,7 @@ def Polygon(pos=(0, 0, 0), normal=(0, 0, 1), nsides=6, r=1, c="coral",
:param followcam: if `True` the text will auto-orient itself to the active camera.
A ``vtkCamera`` object can also be passed.
- :type followcam: bool, vtkCamera
+ :type followcam: bool, vtkCamera
|Polygon|
"""
@@ -760,8 +776,8 @@ def Spheres(centers, r=1, c="r", alpha=1, res=8):
ucols.SetName("colors")
for i, p in enumerate(centers):
vpts.SetPoint(i, p)
- cc = np.array(colors.getColor(c[i])) * 255
- ucols.InsertNextTuple3(cc[0], cc[1], cc[2])
+ cx, cy, cz = colors.getColor(c[i])
+ ucols.InsertNextTuple3(cx * 255, cy * 255, cz * 255)
pd.GetPointData().SetScalars(ucols)
glyph.ScalingOff()
elif risseq:
@@ -837,7 +853,8 @@ def Earth(pos=(0, 0, 0), r=1, lw=1):
return ass
-def Ellipsoid(pos=(0, 0, 0), axis1=(1, 0, 0), axis2=(0, 2, 0), axis3=(0, 0, 3), c="c", alpha=1, res=24):
+def Ellipsoid(pos=(0, 0, 0), axis1=(1, 0, 0), axis2=(0, 2, 0), axis3=(0, 0, 3),
+ c="c", alpha=1, res=24):
"""
Build a 3D ellipsoid centered at position `pos`.
@@ -933,7 +950,7 @@ def Plane(pos=(0, 0, 0), normal=(0, 0, 1), sx=1, sy=None, c="g", bc="darkgreen",
"""
Draw a plane of size `sx` and `sy` oriented perpendicular to vector `normal`
and so that it passes through point `pos`.
-
+
|Plane|
"""
if sy is None:
@@ -1195,7 +1212,7 @@ def Paraboloid(pos=(0, 0, 0), r=1, height=1, axis=(0, 0, 1), c="cyan", alpha=1,
Full volumetric expression is:
:math:`F(x,y,z)=a_0x^2+a_1y^2+a_2z^2+a_3xy+a_4yz+a_5xz+ a_6x+a_7y+a_8z+a_9`
- |paraboloid|
+ |paraboloid|
"""
quadric = vtk.vtkQuadric()
quadric.SetCoefficients(1, 1, 0, 0, 0, 0, 0, 0, height / 4, 0)
@@ -1286,7 +1303,7 @@ def Text(
s=1,
depth=0.1,
justify="bottom-left",
- c=(0.6, 0.6, 0.6),
+ c=None,
alpha=1,
bc=None,
bg=None,
@@ -1297,7 +1314,18 @@ def Text(
Returns a ``vtkActor`` that shows a 3D text.
:param pos: position in 3D space,
- if an integer is passed [1,8], place a 2D text in one of the 4 corners.
+ if an integer is passed [1,8],
+ a 2D text is placed in one of the 4 corners:
+
+ 1, bottom-left
+ 2, bottom-right
+ 3, top-left
+ 4, top-right
+ 5, bottom-middle
+ 6, middle-right
+ 7, middle-left
+ 8, top-middle
+
:type pos: list, int
:param float s: size of text.
:param float depth: text thickness.
@@ -1314,15 +1342,25 @@ def Text(
|markpoint| |markpoint.py|_
|annotations.py|_ Read a text file and shows it in the rendering window.
- """
+ """
+ if c is None: # automatic black or white
+ if settings.plotter_instance and settings.plotter_instance.renderer:
+ c = (0.9, 0.9, 0.9)
+ if np.sum(settings.plotter_instance.renderer.GetBackground()) > 1.5:
+ c = (0.1, 0.1, 0.1)
+ else:
+ c = (0.6, 0.6, 0.6)
+
if isinstance(pos, int):
if pos > 8:
pos = 8
if pos < 1:
pos = 1
+
ca = vtk.vtkCornerAnnotation()
ca.SetNonlinearFontScaleFactor(s / 3)
ca.SetText(pos - 1, str(txt))
+
ca.PickableOff()
cap = ca.GetTextProperty()
cap.SetColor(colors.getColor(c))
@@ -1338,6 +1376,8 @@ def Text(
cap.SetBackgroundOpacity(alpha * 0.5)
cap.SetFrameColor(bgcol)
cap.FrameOn()
+
+ setattr(ca, 'renderedAt', set())
settings.collectable_actors.append(ca)
return ca
@@ -1416,10 +1456,11 @@ def Latex(
bg=None,
alpha=1,
res=30,
+ usetex=False,
fromweb=False,
):
"""
- Latex formulas renderer.
+ Render Latex formulas.
:param str formula: latex text string
:param list pos: position coordinates in space
@@ -1427,85 +1468,91 @@ def Latex(
:param c: face color
:param bg: background color box
:param int res: dpi resolution
- :param fromweb: retrieve the latex image from online server
+ :param bool usetex: use latex compiler of matplotlib
+ :param fromweb: retrieve the latex image from online server (codecogs)
.. hint:: |latex| |latex.py|_
"""
try:
- import matplotlib.pyplot as plt
- from PIL import Image #, ImageChops
- from vtkplotter.actors import ImageActor
- import io, os
- except:
- colors.printc('~times Latex Error: matplotlib and/or pillow not installed?', c=1)
- return None
-
- def build_img_web(formula, tfile):
- import requests
- if c == 'k':
- ct = 'Black'
- else:
- ct = 'White'
- wsite = 'http://latex.codecogs.com/png.latex'
- r = requests.get(wsite+'?\dpi{200} \huge \color{'+ct+'} ' + formula)
- f = open(tfile, 'wb')
- f.write(r.content)
- f.close()
-
- def build_img_plt(formula):
- buf = io.BytesIO()
- plt.rc('text', usetex=True)
- plt.axis('off')
- col = colors.getColor(c)
- if bg:
- bx = dict(boxstyle="square", ec=col, fc=colors.getColor(bg))
- else:
- bx = None
- plt.text(0.5, 0.5, f'${formula}$',
- size=res,
- color=col,
- alpha=alpha,
- ha="center",
- va="center",
- bbox=bx)
- plt.savefig(buf, format='png', transparent=True, bbox_inches='tight', pad_inches=0)
- plt.close()
- im = Image.open(buf)
- return im
-
- if fromweb:
- build_img_web(formula, 'lateximg.png')
- else:
- try:
- build_img_plt(formula).save('lateximg.png')
- except RuntimeError as err:
- colors.printc(err, c=1)
- colors.printc('dvipng not installed? Try > sudo apt install dvipng' , c=1)
- return None
-
- picr = vtk.vtkPNGReader()
- picr.SetFileName('lateximg.png')
- picr.Update()
- vactor = ImageActor()
- vactor.SetInputData(picr.GetOutput())
- vactor.alpha(alpha)
- b = vactor.GetBounds()
- xm, ym = (b[1]+b[0])/200*s, (b[3]+b[2])/200*s
- vactor.SetOrigin(-xm, -ym, 0)
- nax = np.linalg.norm(normal)
- if nax:
- normal = np.array(normal) / nax
- theta = np.arccos(normal[2])
- phi = np.arctan2(normal[1], normal[0])
- vactor.SetScale(0.25/res*s, 0.25/res*s, 0.25/res*s)
- vactor.RotateZ(phi * 57.3)
- vactor.RotateY(theta * 57.3)
- vactor.SetPosition(pos)
- os.unlink('lateximg.png')
- return vactor
-
+
+ #def _Latex(formula, pos, normal, c, s, bg, alpha, res, usetex, fromweb):
+ def build_img_web(formula, tfile):
+ import requests
+ if c == 'k':
+ ct = 'Black'
+ else:
+ ct = 'White'
+ wsite = 'http://latex.codecogs.com/png.latex'
+ try:
+ r = requests.get(wsite+'?\dpi{100} \huge \color{'+ct+'} ' + formula)
+ f = open(tfile, 'wb')
+ f.write(r.content)
+ f.close()
+ except requests.exceptions.ConnectionError:
+ colors.printc('Latex error. Web site unavailable?', wsite, c=1)
+ return None
+ def build_img_plt(formula, tfile):
+ import matplotlib.pyplot as plt
+ plt.rc('text', usetex=usetex)
+ formula1 = '$'+formula+'$'
+ plt.axis('off')
+ col = colors.getColor(c)
+ if bg:
+ bx = dict(boxstyle="square", ec=col, fc=colors.getColor(bg))
+ else:
+ bx = None
+ plt.text(0.5, 0.5, formula1,
+ size=res,
+ color=col,
+ alpha=alpha,
+ ha="center",
+ va="center",
+ bbox=bx)
+ plt.savefig('_lateximg.png', format='png',
+ transparent=True, bbox_inches='tight', pad_inches=0)
+ plt.close()
+ if fromweb:
+ build_img_web(formula, '_lateximg.png')
+ else:
+ build_img_plt(formula, '_lateximg.png')
+
+ from vtkplotter.actors import ImageActor
+
+ picr = vtk.vtkPNGReader()
+ picr.SetFileName('_lateximg.png')
+ picr.Update()
+ vactor = ImageActor()
+ vactor.SetInputData(picr.GetOutput())
+ vactor.alpha(alpha)
+ b = vactor.GetBounds()
+ xm, ym = (b[1]+b[0])/200*s, (b[3]+b[2])/200*s
+ vactor.SetOrigin(-xm, -ym, 0)
+ nax = np.linalg.norm(normal)
+ if nax:
+ normal = np.array(normal) / nax
+ theta = np.arccos(normal[2])
+ phi = np.arctan2(normal[1], normal[0])
+ vactor.SetScale(0.25/res*s, 0.25/res*s, 0.25/res*s)
+ vactor.RotateZ(phi * 57.3)
+ vactor.RotateY(theta * 57.3)
+ vactor.SetPosition(pos)
+ try:
+ import os
+ os.unlink('_lateximg.png')
+ except FileNotFoundError:
+ pass
+ return vactor
+
+ except:
+ colors.printc('Error in Latex()\n', formula, c=1)
+ colors.printc(' latex or dvipng not installed?', c=1)
+ colors.printc(' Try: usetex=False' , c=1)
+ colors.printc(' Try: sudo apt install dvipng' , c=1)
+ return None
+
+
diff --git a/vtkplotter/utils.py b/vtkplotter/utils.py
index e6c8c4ed..30c6f820 100644
--- a/vtkplotter/utils.py
+++ b/vtkplotter/utils.py
@@ -43,15 +43,6 @@ def isSequence(arg):
return False
-# def flatten(lst):
-# '''Flatten out a list'''
-# flat_list = []
-# for sublist in lst:
-# for item in sublist:
-# flat_list.append(item)
-# return flat_list
-
-
def flatten(list_to_flatten):
"""Flatten out a list."""
@@ -452,6 +443,7 @@ def printvtkactor(actor, tab=""):
7: "(ruler at the bottom of the window)",
8: "(the vtkCubeAxesActor object)",
9: "(the bounding box outline)",
+ 10: "(circles of maximum bounding box range)",
}
bns, totpt = [], 0
for a in obj.actors:
@@ -532,3 +524,10 @@ def makeBands(inputlist, numberOfBands):
break
return np.array(newlist)
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vtkplotter/version.py b/vtkplotter/version.py
new file mode 100644
index 00000000..d473e7a5
--- /dev/null
+++ b/vtkplotter/version.py
@@ -0,0 +1 @@
+_version='2019.1.5'
diff --git a/vtkplotter/vtkio.py b/vtkplotter/vtkio.py
index c02bc64d..fe8c0915 100644
--- a/vtkplotter/vtkio.py
+++ b/vtkplotter/vtkio.py
@@ -10,6 +10,7 @@
from vtkplotter.actors import Actor, Assembly, ImageActor, isosurface
import vtkplotter.docs as docs
import vtkplotter.settings as settings
+import vtkplotter.addons as addons
__doc__ = (
"""
@@ -57,7 +58,7 @@ def load(
threshold=None,
connectivity=False,
):
- """
+ """
Returns a ``vtkActor`` from reading a file, directory or ``vtkPolyData``.
:param c: color in RGB format, hex, symbol or name
@@ -119,7 +120,7 @@ def _loadFile(filename, c, alpha, wire, bc, texture, smoothing, threshold, conne
actor = loadGmesh(filename, c, alpha, wire, bc)
elif fl.endswith(".pcd"): # PCL point-cloud format
actor = loadPCD(filename, c, alpha)
- elif fl.endswith(".off"):
+ elif fl.endswith(".off"):
actor = loadOFF(filename, c, alpha, wire, bc)
elif fl.endswith(".3ds"): # 3ds point-cloud format
actor = load3DS(filename)
@@ -221,7 +222,7 @@ def loadMultiBlockData(filename, unpack=True):
acts = []
for i in range(mb.GetNumberOfBlocks()):
b = mb.GetBlock(i)
- if isinstance(b, (vtk.vtkPolyData,
+ if isinstance(b, (vtk.vtkPolyData,
vtk.vtkImageData,
vtk.vtkUnstructuredGrid,
vtk.vtkStructuredGrid,
@@ -524,8 +525,6 @@ def loadImageData(filename, spacing=()):
reader.SetFileName(filename)
reader.Update()
image = reader.GetOutput()
- print(filename, "scalar range:", image.GetScalarRange())
- #colors.printHistogram()
if len(spacing) == 3:
image.SetSpacing(spacing[0], spacing[1], spacing[2])
return image
@@ -584,7 +583,7 @@ def write(objct, fileoutput, binary=True):
g = vtk.vtkMultiBlockDataGroupFilter()
for ob in objct:
g.AddInputData(ob)
- g.Update()
+ g.Update()
mb = g.GetOutputDataObject(0)
wri = vtk.vtkXMLMultiBlockDataWriter()
wri.SetInputData(mb)
@@ -792,7 +791,12 @@ def buildPolyData(vertices, faces=None, indexOffset=0):
##########################################################
def screenshot(filename="screenshot.png"):
- """Save a screenshot of the current rendering window."""
+ """
+ Save a screenshot of the current rendering window.
+ """
+ if not settings.plotter_instance.window:
+ colors.printc('~bomb screenshot(): Rendering window is not present, skip.', c=1)
+ return
w2if = vtk.vtkWindowToImageFilter()
w2if.ShouldRerenderOff()
w2if.SetInput(settings.plotter_instance.window)
@@ -1051,7 +1055,7 @@ def switch(self):
# ############################################################### Mouse Events
def _mouse_enter(iren, event):
- x, y = iren.GetEventPosition()
+ #x, y = iren.GetEventPosition()
#print('_mouse_enter mouse at', x, y)
for ivp in settings.plotter_instances:
@@ -1396,7 +1400,7 @@ def _keypress(iren, event):
ia.GetProperty().SetColor(colors.colors1[(i + vp.icol) % 10])
ia.GetMapper().ScalarVisibilityOff()
vp.icol += 1
- vp.addLegend()
+ addons.addLegend()
elif key == "2":
if vp.clickedActor and hasattr(vp.clickedActor, "GetProperty"):
@@ -1408,7 +1412,7 @@ def _keypress(iren, event):
ia.GetProperty().SetColor(colors.colors2[(i + vp.icol) % 10])
ia.GetMapper().ScalarVisibilityOff()
vp.icol += 1
- vp.addLegend()
+ addons.addLegend()
elif key == "3":
c = colors.getColor("gold")
@@ -1420,7 +1424,7 @@ def _keypress(iren, event):
ia.GetProperty().SetColor(c)
ia.GetProperty().SetOpacity(alpha)
ia.GetMapper().ScalarVisibilityOff()
- vp.addLegend()
+ addons.addLegend()
elif key == "4":
bgc = numpy.array(vp.renderer.GetBackground()).sum() / 3
@@ -1449,7 +1453,7 @@ def _keypress(iren, event):
else:
vp.renderer.RemoveActor(vp.axes_exist[clickedr])
vp.axes_exist[clickedr] = None
- vp.addAxes(axtype=asso[key], c=None)
+ addons.addAxes(axtype=asso[key], c=None)
vp.interactor.Render()
elif key in ["k", "K"]:
@@ -1556,12 +1560,12 @@ def _keypress(iren, event):
vp.renderer.AddActor(vp.justremoved)
vp.renderer.Render()
vp.justremoved = None
- vp.addLegend()
+ addons.addLegend()
elif key == "X":
if vp.clickedActor:
if not vp.cutterWidget:
- vp.addCutterTool(vp.clickedActor)
+ addons.addCutterTool(vp.clickedActor)
else:
fname = "clipped.vtk"
confilter = vtk.vtkPolyDataConnectivityFilter()
@@ -1587,9 +1591,9 @@ def _keypress(iren, event):
else:
for a in vp.actors:
if isinstance(a, vtk.vtkVolume):
- vp.addCutterTool(a)
+ addons.addCutterTool(a)
return
-
+
colors.printc("Click an actor and press X to open the cutter box widget.", c=4)
elif key == "i": # print info