diff --git a/bin/vtkplotter b/bin/vtkplotter
index 750995e5..68fec0a0 100755
--- a/bin/vtkplotter
+++ b/bin/vtkplotter
@@ -281,7 +281,7 @@ def draw_scene():
vp.window.AddObserver("AbortCheckEvent", CheckAbort)
# add histogram of scalar
- from vtkplotter import histogram
+ from vtkplotter.analysis import histogram2D
dims = img.GetDimensions()
nvx = min(100000, dims[0] * dims[1] * dims[2])
@@ -292,7 +292,7 @@ def draw_scene():
d = img.GetScalarComponentAsFloat(ix, iy, iz, 0)
data.append(d)
- plot = histogram(
+ plot = histogram2D(
data, bins=40, logscale=1, c="gray", bg="gray", pos=(0.78, 0.065)
)
plot.GetPosition2Coordinate().SetValue(0.197, 0.20, 0)
diff --git a/examples/advanced/fitplanes.py b/examples/advanced/fitplanes.py
index b27be4b9..75f6a5dd 100644
--- a/examples/advanced/fitplanes.py
+++ b/examples/advanced/fitplanes.py
@@ -26,7 +26,7 @@
vp += Arrow(cn, cn + v / 15.0, c="g")
variances.append(plane.info["variance"])
-vp += histogram(variances, title="variance", c="g")
+vp += histogram(variances, c="g").scale([30,.03,30]).pos(-0.5, -1.2, -.6)
vp += Text(__doc__, pos=1)
vp.show(viewup="z")
diff --git a/examples/advanced/fitspheres1.py b/examples/advanced/fitspheres1.py
index f52cdfc6..f1d864a4 100644
--- a/examples/advanced/fitspheres1.py
+++ b/examples/advanced/fitspheres1.py
@@ -4,8 +4,6 @@
For some of these point we show the fitting sphere.
Red lines join the center of the sphere to the surface point.
Blue points are the N points used for fitting.
-Green histogram is the distribution of residuals from the fitting.
-Red histogram is the distribution of the curvatures (1/r**2).
Fitted radius can be accessed from actor.info['radius'].
"""
from __future__ import division, print_function
@@ -16,7 +14,6 @@
# load mesh and increase by a lot (N=2) the nr of surface vertices
s = vp.load(datadir+"cow.vtk").alpha(0.3).subdivide(N=2)
-reds, invr = [], []
for i, p in enumerate(s.getPoints()):
if i % 1000:
continue # skip most points
@@ -27,11 +24,6 @@
vp += sph
vp += Points(pts)
vp += Line(sph.info["center"], p, lw=2)
- reds.append(sph.info["residue"])
- invr.append(1 / sph.info["radius"] ** 2)
-
-vp += histogram(reds, title="residue", bins=12, c="g", pos=3)
-vp += histogram(invr, title="1/r**2", bins=12, c="r", pos=4)
vp += Text(__doc__)
vp.show(viewup="z")
diff --git a/examples/basic/README.md b/examples/basic/README.md
index 9994e828..ba8374df 100644
--- a/examples/basic/README.md
+++ b/examples/basic/README.md
@@ -41,7 +41,7 @@ python example.py
| | |
| [![fxy](https://user-images.githubusercontent.com/32848391/50738863-bfccf800-11d8-11e9-882d-7b217aceb55a.jpg)](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/fxy.py)
`fxy.py` | Draw a surface representing a function _f(x, y)_ defined as a string/formula or as a reference to an external already existing function.
Red points indicate where the function does not exist. |
| | |
-| [![histo2d](https://user-images.githubusercontent.com/32848391/50738861-bfccf800-11d8-11e9-9698-c0b9dccdba4d.jpg)](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/histo2D.py)
`histo2D.py` | Make a histogram of two variables with hexagonal binning. |
+| [![histo2d](https://user-images.githubusercontent.com/32848391/50738861-bfccf800-11d8-11e9-9698-c0b9dccdba4d.jpg)](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/histo2D.py)
`histoHexagonal.py` | Make a histogram of two variables with hexagonal binning. |
| | |
| [![keypress](https://user-images.githubusercontent.com/32848391/50738860-bfccf800-11d8-11e9-96ca-dab2bb7adae3.jpg)](https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/keypress.py)
`keypress.py` | How to implement a custom function that is triggered by pressing a keyboard button when the rendering window is in interactive mode.
In the example, every time a key is pressed the picked point of the mesh is used to add a sphere and some info is printed. |
| | |
diff --git a/examples/basic/align2.ipynb b/examples/basic/align2.ipynb
index 66f7ea29..9b560784 100644
--- a/examples/basic/align2.ipynb
+++ b/examples/basic/align2.ipynb
@@ -8,12 +8,12 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "c36f73641ab1421fab19091a99c5f175",
+ "model_id": "4177c602a52e48148251a3635bd676ac",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
- "Plot(antialias=3, axes=['x', 'y', 'z'], background_color=16777215, camera=[2.1504162283002834, 4.9575156559299…"
+ "Plot(antialias=3, axes=['x', 'y', 'z'], background_color=16777215, camera=[2.238617303212521, 5.12911044410189…"
]
},
"metadata": {},
diff --git a/examples/basic/connVtx.ipynb b/examples/basic/connVtx.ipynb
index 7ce76776..80692d63 100644
--- a/examples/basic/connVtx.ipynb
+++ b/examples/basic/connVtx.ipynb
@@ -8,7 +8,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "6afe2573e4ed40789d86033940337f3c",
+ "model_id": "179089faa27c43a3aee25c8405ceb299",
"version_major": 2,
"version_minor": 0
},
@@ -61,7 +61,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.3"
+ "version": "3.6.8"
}
},
"nbformat": 4,
diff --git a/examples/basic/donutPlot.py b/examples/basic/donutPlot.py
new file mode 100644
index 00000000..a89c142e
--- /dev/null
+++ b/examples/basic/donutPlot.py
@@ -0,0 +1,10 @@
+from vtkplotter import donutPlot
+
+title = "A donut plot"
+fractions = [0.1, 0.2, 0.3, 0.1, 0.3]
+colors = [ 1, 2, 3, 4, 'white']
+labels = ["stuff1", "stuff2", "compA", "compB", ""]
+
+dn = donutPlot(fractions, c=colors, labels=labels, title=title)
+
+dn.show(axes=None, bg='w')
diff --git a/examples/basic/fxy.ipynb b/examples/basic/fxy.ipynb
index 0b5e9438..57d03632 100644
--- a/examples/basic/fxy.ipynb
+++ b/examples/basic/fxy.ipynb
@@ -2,18 +2,18 @@
"cells": [
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "1a76cbee3deb4be691a8ef693570473c",
+ "model_id": "3f3e2d4fa8d146b8ad69c3d498145f2a",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
- "Plot(antialias=3, axes=['x', 'y', 'z'], background_color=16777215, camera=[3.794147738309014, 3.79414773830901…"
+ "Plot(antialias=3, axes=['x', 'y', 'z'], background_color=16777215, camera=[3.760363867861654, 3.76036386786165…"
]
},
"metadata": {},
@@ -21,16 +21,17 @@
}
],
"source": [
- "\"\"\"Example for fxy() method.\n",
- "Draw a surface representing the 3D function specified as a string\n",
- "or as a reference to an external already existing function.\n",
- "Red points indicate where the function does not exist.\"\"\"\n",
+ "\"\"\"\n",
+ "Draw a surface representing a 2-var function specified \n",
+ "as a string or as a reference to an external existing function.\n",
+ "\"\"\"\n",
"from vtkplotter import Plotter, fxy, sin, cos\n",
"\n",
"def my_z(x, y):\n",
" return sin(2 * x * y) * cos(3 * y) / 2\n",
"\n",
- "f1 = fxy(my_z, zlevels=0, showNan=False).c('lightblue')\n",
+ "f1 = fxy(my_z, zlevels=0).c('lightblue')\n",
+ "#f1 = fxy(\"sin(2*x*y)*cos(3*y)/2\", zlevels=0).c('lightblue')\n",
"\n",
"f1.show(viewup='z')"
]
@@ -59,7 +60,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.7.3"
+ "version": "3.6.8"
}
},
"nbformat": 4,
diff --git a/examples/basic/fxy.py b/examples/basic/fxy.py
index 968a7ce4..bddaebde 100644
--- a/examples/basic/fxy.py
+++ b/examples/basic/fxy.py
@@ -1,26 +1,22 @@
"""
-Example for function() method.
-Draw a surface representing the 3D function specified as a string
-or as a reference to an external already existing function.
+Draw a surface representing a 2-var function specified
+as a string or as a reference to an external existing function.
Red points indicate where the function does not exist.
"""
print(__doc__)
-from vtkplotter import Plotter, fxy, sin, cos, show
+from vtkplotter import fxy, sin, cos, show
def my_z(x, y):
return sin(2 * x * y) * cos(3 * y) / 2
-
-# draw at renderer nr.0 the first actor, show it with a texture
# an existing function z(x,y) can be passed:
f1 = fxy(my_z)
# red dots are shown where the function does not exist (y>x):
-# if vp is set to verbose, sympy calculates derivatives and prints them:
f2 = fxy("sin(3*x)*log(x-y)/3")
# specify x and y ranges and z vertical limits:
-f3 = fxy("log(x**2+y**2 - 1)", x=[-2, 2], y=[-2, 2], zlimits=[-1, 1.5])
+f3 = fxy("log(x**2+y**2-1)", x=[-2,2], y=[-1,8], zlimits=[-1,None])
show(f1, f2, f3, N=3, axes=1, sharecam=False, bg="w")
diff --git a/examples/basic/histo2D.py b/examples/basic/histo2D.py
deleted file mode 100644
index 183fedb1..00000000
--- a/examples/basic/histo2D.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""
-2D histogram with hexagonal binning.
-"""
-from vtkplotter import *
-import numpy as np
-
-vp = Plotter(axes=1, verbose=0, bg="w")
-vp.xtitle = "x gaussian, s=1.0"
-vp.ytitle = "y gaussian, s=1.5"
-vp.ztitle = "dN/dx/dy"
-
-N = 20000
-x = np.random.randn(N) * 1.0
-y = np.random.randn(N) * 1.5
-
-histo = histogram2D(x, y, bins=10, fill=True)
-
-pts = Points([x, y, np.zeros(N)+6], c="black", alpha=0.01)
-
-f = r'f(x, y)=A \exp \left(-\left(\frac{\left(x-x_{o}\right)^{2}}'
-f+= r'{2 \sigma_{x}^{2}}+\frac{\left(y-y_{o}\right)^{2}}'
-f+= r'{2 \sigma_{y}^{2}}\right)\right)'
-
-formula = Latex(f, c='k', s=1.5).rotateZ(90).rotateX(90).pos(1,-1,1)
-
-vp.show(histo, pts, formula, Text(__doc__), viewup="z")
diff --git a/examples/basic/histoHexagonal.py b/examples/basic/histoHexagonal.py
new file mode 100644
index 00000000..1611ef3a
--- /dev/null
+++ b/examples/basic/histoHexagonal.py
@@ -0,0 +1,25 @@
+"""2D histogram with hexagonal binning."""
+from vtkplotter import *
+import numpy as np
+
+N = 2000
+x = np.random.randn(N) * 1.0
+y = np.random.randn(N) * 1.5
+
+# hexagonal histogram
+histo = hexHistogram(x, y, bins=10, fill=True, cmap='terrain')
+
+# scatter plot:
+pts = Points([x, y, np.zeros(N)+6], c="black", alpha=0.05)
+
+f = r'f(x, y)=A \exp \left(-\left(\frac{\left(x-x_{o}\right)^{2}}'
+f+= r'{2 \sigma_{x}^{2}}+\frac{\left(y-y_{o}\right)^{2}}'
+f+= r'{2 \sigma_{y}^{2}}\right)\right)'
+formula = Latex(f, c='k', s=1.5).rotateZ(90).rotateX(90).pos(1,-1,1)
+
+#settings.useParallelProjection = True
+settings.xtitle = "x gaussian, s=1.0"
+settings.ytitle = "y gaussian, s=1.5"
+settings.ztitle = "dN/dx/dy"
+
+show(histo, pts, formula, Text(__doc__), axes=1, verbose=0, bg="white")
diff --git a/examples/basic/histoPolar.py b/examples/basic/histoPolar.py
new file mode 100644
index 00000000..6a574a40
--- /dev/null
+++ b/examples/basic/histoPolar.py
@@ -0,0 +1,41 @@
+from vtkplotter import polarHistogram, Hyperboloid, show
+import numpy as np
+np.random.seed(3)
+
+
+##################################################################
+radhisto = polarHistogram(np.random.rand(200)*6.28,
+ title="random orientations",
+ bins=10,
+ #c='orange', #uniform color
+ labels=["label"+str(i) for i in range(10)],
+ )
+
+show(radhisto, at=0, N=2, axes=0, sharecam=False, bg="white")
+
+
+##################################################################
+hyp = Hyperboloid(res=20).cutWithPlane().rotateY(-90)
+hyp.color('grey').alpha(0.3)
+
+# select 10 random indeces of points on the surface
+idx = np.random.randint(0, hyp.NPoints(), size=10)
+
+radhistos = []
+for i in idx:
+ #generate a random histogram
+ rh = polarHistogram(np.random.randn(100),
+ bins=12,
+ r1=0.2, # inner radius
+ phigap=1.0, # leave a space btw phi bars
+ cmap='viridis_r',
+ showDisc=False,
+ showAngles=False,
+ showErrors=False,
+ )
+ rh.scale(0.15) # scale histogram to make it small
+ rh.pos(hyp.getPoint(i)) # set its position on the surface
+ rh.orientation(hyp.normalAt(i)) # orient it along normal
+ radhistos.append(rh)
+
+show(hyp, radhistos, at=1, interactive=True)
\ No newline at end of file
diff --git a/examples/basic/histogram.py b/examples/basic/histogram.py
new file mode 100644
index 00000000..4856193b
--- /dev/null
+++ b/examples/basic/histogram.py
@@ -0,0 +1,14 @@
+from vtkplotter import *
+import numpy as np
+
+data1 = np.random.randn(500)*3+10
+data2 = np.random.randn(500)*2+ 7
+
+h1 = histogram(data1, fill=True, outline=False, errors=True)
+h2 = histogram(data2, fill=False, lc='firebrick', lw=4)
+h2.z(0.1) # put h2 in front of h1
+
+h1.scale([1, 0.2, 1]) # set a common y-scaling factor
+h2.scale([1, 0.2, 1])
+
+show(h1, h2, bg='white', axes=1)
\ No newline at end of file
diff --git a/examples/basic/multiwindows.py b/examples/basic/multiwindows.py
index 7a4a2fb8..3de796b7 100644
--- a/examples/basic/multiwindows.py
+++ b/examples/basic/multiwindows.py
@@ -1,41 +1,50 @@
"""
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
-in specific windows numbers.
+We split the main window in many subwindows and draw
+somethingon specific windows numbers.
Then open an independent window and draw a shape on it.
"""
print(__doc__)
+from vtkplotter import *
-from vtkplotter import Plotter, Text, datadir
-
-
+##########################################################################
# this is one instance of the class Plotter with 5 raws and 5 columns
-vp1 = Plotter(shape=(5, 5), axes=0, bg="white")
-# having set shape=(n,m), script execution after the show() is not held
+vp1 = Plotter(shape=(5,5), axes=0, bg="white")
# set a different background color for a specific subwindow (the last one)
vp1.renderers[24].SetBackground(0.8, 0.9, 0.9) # use vtk method SetBackground()
# load the actors and give them a name
-a = vp1.load(datadir+"airboat.vtk").legend("some legend")
+a = vp1.load(datadir+"airboat.vtk")
b = vp1.load(datadir+"cessna.vtk", c="red")
c = vp1.load(datadir+"atc.ply")
# show a text in each renderer
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)
+ txt = Text("renderer\nnr."+str(i), c='k', font='arial', s=1)
+ vp1.show(txt, at=i)
-vp1.show(a, at=22)
+vp1.show(a, at= 6)
vp1.show(b, at=23)
-vp1.show(c, at=24, interactive=0)
+vp1.show(c, at=24)
-############################################################
+##########################################################################
# declare a second independent instance of the class Plotter
-vp2 = Plotter(pos=(500, 250), bg=(0.9, 0.9, 1)) # blue-ish background
+# shape can also be given as a string, e.g.:
+# shape="2/6" means 2 renderers above and 6 below
+# shape="3|1" means 3 renderers on the left and one on the right
+
+s = load(datadir+'pumpkin.vtk')
+
+#settings.multiRenderingSplittingPosition = 0.5
+
+vp2 = Plotter(pos=(500, 250), shape='2/6')
-vp2.load(datadir+"porsche.ply").legend("an other window")
+for i in range(len(vp2.renderers)):
+ s2 = s.clone().color(i)
+ txt = Text('renderer #'+str(i))
+ vp2.show(s2, txt, at=i)
-vp2.show() # show and interact with mouse and keyboard
+interactive()
diff --git a/examples/basic/polarPlot.py b/examples/basic/polarPlot.py
new file mode 100644
index 00000000..7690abc0
--- /dev/null
+++ b/examples/basic/polarPlot.py
@@ -0,0 +1,15 @@
+from vtkplotter import polarPlot, show
+import numpy as np
+
+title = "A (splined) polar plot"
+angles = [ 0, 20, 60, 160, 200, 250, 300, 340]
+distances = [0.1, 0.2, 0.3, 0.5, 0.6, 0.4, 0.2, 0.1]
+
+dn1 = polarPlot([angles, distances], deg=True, spline=False,
+ c='green', alpha=0.5, title=title, vmax=0.6)
+
+dn2 = polarPlot([angles, np.array(distances)/2], deg=True,
+ c='tomato', alpha=1, showDisc=False, vmax=0.6)
+dn2.z(0.01) # set z above 0, so that plot is visible
+
+show(dn1, dn2, axes=None, bg='white')
diff --git a/examples/notebooks/sphere.ipynb b/examples/notebooks/sphere.ipynb
index 01650693..320e4c6d 100644
--- a/examples/notebooks/sphere.ipynb
+++ b/examples/notebooks/sphere.ipynb
@@ -8,7 +8,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "62ea648105d74f76ac8d8d4bc9f8bb99",
+ "model_id": "6328d5a8448840f88ee1d5aa771fb292",
"version_major": 2,
"version_minor": 0
},
diff --git a/examples/notebooks/test_itkwidgets.ipynb b/examples/notebooks/test_itkwidgets.ipynb
index 4d6d389c..c2719798 100644
--- a/examples/notebooks/test_itkwidgets.ipynb
+++ b/examples/notebooks/test_itkwidgets.ipynb
@@ -2,20 +2,107 @@
"cells": [
{
"cell_type": "code",
- "execution_count": 9,
- "metadata": {
- "scrolled": true
- },
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Install dependencies for this example\n",
+ "# Note: This does not include itkwidgets, itself\n",
+ "import sys\n",
+ "!{sys.executable} -m pip install vtkplotter"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from vtkplotter import *\n",
+ "\n",
+ "embedWindow('itkwidgets')\n",
+ "\n",
+ "plt = Plotter()\n",
+ "\n",
+ "#cyl = Cylinder()\n",
+ "#sph = Sphere().pos(1.5,1,1)\n",
+ "#sph.pointColors(sph.getPoints()[:,2])\n",
+ "embryo = load(datadir+'embryo.slc')"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#plt += sph # test vtkActors\n",
+ "#plt += cyl\n",
+ "#plt += cyl+sph # test vtkAssembly\n",
+ "plt += embryo # test vtkVolume"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\u001b[34m_________________________________________________________________\u001b[0m\n",
+ "\u001b[7m\u001b[1m\u001b[34mvtkVolume\u001b[0m \n",
+ "\u001b[1m\u001b[34m position: \u001b[0m\u001b[34m(0.0, 0.0, 0.0)\u001b[0m\n",
+ "\u001b[1m\u001b[34m dimensions: \u001b[0m\u001b[34m(256, 256, 256)\u001b[0m\n",
+ "\u001b[1m\u001b[34m spacing: \u001b[0m\u001b[34m(1.0, 1.0, 1.0)\u001b[0m\n",
+ "\u001b[1m\u001b[34m data dimension: \u001b[0m\u001b[34m3\u001b[0m\n",
+ "\u001b[1m\u001b[34m memory size: \u001b[0m\u001b[34m16 Mb\u001b[0m\n",
+ "\u001b[1m\u001b[34m scalar #bytes: \u001b[0m\u001b[34m1\u001b[0m\n",
+ "\u001b[1m\u001b[34m bounds: \u001b[0m\u001b[34mx=(0, 255)\u001b[0m\u001b[34m y=(0, 255)\u001b[0m\u001b[34m z=(0, 255)\u001b[0m\n",
+ "\u001b[1m\u001b[34m scalar range: \u001b[0m\u001b[34m(0.0, 197.0)\u001b[0m\n",
+ "\u001b[34m(logscale) Histogram\t(entries=100000)\n",
+ " 4.97\n",
+ "0.00 | ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆\n",
+ "20.50 | ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆\n",
+ "41.00 | ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆\n",
+ "61.50 | ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆\n",
+ "82.00 | ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆\n",
+ "102.50| ▆▆▆▆▆▆▆▆▆▆▆▆▆▆\n",
+ "123.00| ▆▆▆▆▆▆▆▆▆▆\n",
+ "143.50| ▆▆▆▆▆▆\n",
+ "\u001b[0m\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "(Volume)0x7f819f83e768"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "embryo.printInfo()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
- "model_id": "6eb4c867b65746d6a476565a977866dd",
+ "model_id": "18be23cdb7594dc4ba945bccd5900b30",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
- "Viewer(cmap='jet', geometries=[{'vtkClass': 'vtkPolyData', 'points': {'vtkClass': 'vtkPoints', 'name': '_point…"
+ "Viewer(cmap='jet', geometries=[], gradient_opacity=0.01, point_sets=[], rendered_image= binmax:
+ binmax = ne
+
+ if cmap is not None:
+ for h in hexs:
+ z = h.GetBounds()[5]
+ col = colors.colorMap(z, cmap, 0, binmax)
+ h.color(col)
+
+ asse = Assembly(hexs)
+ asse.SetScale(1.2 / n * dx, 1/m*dy, norm/binmax * (dx+dy) / 4)
+ asse.SetPosition(xmin, ymin, 0)
+ return asse
+
+
+def polarHistogram(values,
+ title="",
+ bins=10,
+ r1=0.25,
+ r2=1,
+ phigap=3,
+ rgap=0.05,
+ lpos=1,
+ lsize=0.05,
+ c=None,
+ bc="k",
+ alpha=1,
+ cmap=None,
+ deg=False,
+ vmin=None,
+ vmax=None,
+ labels=(),
+ showDisc=True,
+ showLines=True,
+ showAngles=True,
+ showErrors=False,
+ ):
+ """
+ Polar histogram with errorbars.
+
+ :param str title: histogram title
+ :param int bins: number of bins in phi
+ :param float r1: inner radius
+ :param float r2: outer radius
+ :param float phigap: gap angle btw 2 radial bars, in degrees
+ :param float rgap: gap factor along radius of numeric angle labels
+ :param float lpos: label gap factor along radius
+ :param float lsize: label size
+ :param c: color of the histogram bars, can be a list of length `bins`.
+ :param bc: color of the frame and labels
+ :param alpha: alpha of the frame
+ :param str cmap: color map name
+ :param bool deg: input array is in degrees
+ :param float vmin: minimum value of the radial axis
+ :param float vmax: maximum value of the radial axis
+ :param list labels: list of labels, must be of length `bins`
+ :param bool showDisc: show the outer ring axis
+ :param bool showLines: show lines to the origin
+ :param bool showAngles: show angular values
+ :param bool showErrors: show error bars
+
+ |histoPolar| |histoPolar.py|_
+ """
+ k = 180 / np.pi
+ if deg:
+ values = np.array(values)/k
+
+ dp = np.pi/bins
+ vals = []
+ for v in values: # normalize range
+ t = np.arctan2(np.sin(v), np.cos(v))
+ if t<0:
+ t += 2 * np.pi
+ vals.append(t-dp)
+
+ histodata, edges = np.histogram(vals, bins=bins,
+ range=(-dp,2*np.pi-dp))
+ thetas = []
+ for i in range(bins):
+ thetas.append((edges[i] + edges[i+1]) / 2)
+
+ if vmin is None:
+ vmin = np.min(histodata)
+ if vmax is None:
+ vmax = np.max(histodata)
+
+ errors = np.sqrt(histodata)
+ r2e = r1+r2
+ if showErrors:
+ r2e += np.max(errors)/vmax*1.5
+
+ back=None
+ if showDisc:
+ back = shapes.Disc(r1=r2e, r2=r2e*1.01, c=bc, res=1, resphi=360)
+ back.z(-0.01).lighting(diffuse=0, ambient=1).alpha(alpha)
+
+ slices = []
+ lines = []
+ angles = []
+ labs = []
+ errbars = []
+
+ for i, t in enumerate(thetas):
+ r = histodata[i]/vmax*r2
+ d = shapes.Disc((0,0,0), r1, r1+r, res=1, resphi=360)
+ delta = dp - np.pi/2 - phigap/k
+ d.cutWithPlane(normal=(np.cos(t+delta), np.sin(t+delta), 0))
+ d.cutWithPlane(normal=(np.cos(t-delta), np.sin(t-delta), 0))
+ if cmap is not None:
+ cslice = colors.colorMap(histodata[i], cmap, vmin, vmax)
+ d.color(cslice)
+ else:
+ if c is None:
+ d.color(i)
+ elif utils.isSequence(c) and len(c)==bins:
+ d.color(c[i])
+ else:
+ d.color(c)
+ slices.append(d)
+
+ ct, st = np.cos(t), np.sin(t)
+
+ if showErrors:
+ showLines = False
+ err = np.sqrt(histodata[i])/vmax*r2
+ errl = shapes.Line(((r1+r-err)*ct, (r1+r-err)*st, 0.01),
+ ((r1+r+err)*ct, (r1+r+err)*st, 0.01))
+ errl.alpha(alpha).lw(3).color(bc)
+ errbars.append(errl)
+
+ if showDisc:
+ if showLines:
+ l = shapes.Line((0, 0, -0.01), (r2e*ct*1.03, r2e*st*1.03, -0.01))
+ lines.append(l)
+ elif showAngles: # just the ticks
+ l = shapes.Line((r2e*ct*0.98, r2e*st*0.98, -0.01),
+ (r2e*ct*1.03, r2e*st*1.03, -0.01))
+ lines.append(l)
+
+ if showAngles:
+ if 0 <= t < np.pi / 2:
+ ju = "bottom-left"
+ elif t == np.pi / 2:
+ ju = "bottom-center"
+ elif np.pi / 2 < t <= np.pi:
+ ju = "bottom-right"
+ elif np.pi < t < np.pi * 3 / 2:
+ ju = "top-right"
+ elif t == np.pi * 3 / 2:
+ ju = "top-center"
+ else:
+ ju = "top-left"
+ a = shapes.Text(int(t * k), pos=(0, 0, 0), s=lsize, depth=0, justify=ju)
+ a.pos(r2e * ct * (1+rgap), r2e * st * (1+rgap), -0.01)
+ angles.append(a)
+
+ if len(labels)==bins:
+ lab = shapes.Text(labels[i], (0,0,0), s=lsize, depth=0, justify="center")
+ lab.pos(r2e * ct * (1 + rgap) * lpos / 2,
+ r2e * st * (1 + rgap) * lpos / 2, 0.01)
+ labs.append(lab)
+
+ ti = None
+ if title:
+ ti = shapes.Text(title, (0,0,0), s=lsize*2, depth=0, justify="top-center")
+ ti.pos(0, -r2e*1.15, 0.01)
+
+ mrg = merge(back, lines, angles, labs, ti)
+ if mrg:
+ mrg.color(bc).alpha(alpha).lighting(diffuse=0, ambient=1)
+ rh = Assembly(slices + errbars + [mrg])
+ rh.base = np.array([0,0,0])
+ rh.top = np.array([0,0,1])
+ return rh
+
+def donutPlot(fractions,
+ title="",
+ r1=1.7,
+ r2=1,
+ phigap=0,
+ lpos=0.8,
+ lsize=0.15,
+ c=None,
+ bc="k",
+ alpha=1,
+ labels=(),
+ showDisc=False,
+):
+ """
+ Donut plot or pie chart.
+
+ :param str title: plot title
+ :param float r1: inner radius
+ :param float r2: outer radius, starting from r1
+ :param float phigap: gap angle btw 2 radial bars, in degrees
+ :param float lpos: label gap factor along radius
+ :param float lsize: label size
+ :param c: color of the plot slices
+ :param bc: color of the disc frame
+ :param alpha: alpha of the disc frame
+ :param list labels: list of labels
+ :param bool showDisc: show the outer ring axis
+
+ |donutPlot| |donutPlot.py|_
+ """
+ fractions = np.array(fractions)
+ angles = np.add.accumulate(2*np.pi*fractions)
+ angles[-1] = 2*np.pi
+ if angles[-2]>2*np.pi:
+ print("Error in donutPlot(): fractions must sum to 1.")
+ raise RuntimeError
+
+ cols = []
+ for i,th in enumerate(np.linspace(0,2*np.pi, 360, endpoint=False)):
+ for ia,a in enumerate(angles):
+ if th=0:
+ zm = 0.0
+ else:
+ zm = (bb[4] + bb[5]) / 2
nans = np.array(nans) + [0, 0, zm]
- nansact = shapes.Points(nans, c="red", alpha=alpha / 2)
+ nansact = shapes.Points(nans, r=2, c="red", alpha=alpha)
+ nansact.GetProperty().RenderPointsAsSpheresOff()
acts.append(nansact)
if len(acts) > 1:
- asse = Assembly(acts)
- return asse
+ return Assembly(acts)
else:
return actor
-def histogram2D(xvalues, yvalues, bins=12, norm=1, fill=True, c=None, alpha=1):
- """
- Build a 2D hexagonal histogram from a list of x and y values.
-
- :param bool bins: nr of bins for the smaller range in x or y.
- :param float norm: sets a scaling factor for the z axis.
- :param bool fill: draw solid hexagons.
-
- |histo2D| |histo2D.py|_
- """
- xmin, xmax = np.min(xvalues), np.max(xvalues)
- ymin, ymax = np.min(yvalues), np.max(yvalues)
- dx, dy = xmax - xmin, ymax - ymin
-
- if xmax - xmin < ymax - ymin:
- n = bins
- m = np.rint(dy / dx * n / 1.2 + 0.5).astype(int)
- else:
- m = bins
- n = np.rint(dx / dy * m * 1.2 + 0.5).astype(int)
-
- src = vtk.vtkPointSource()
- src.SetNumberOfPoints(len(xvalues))
- src.Update()
- pointsPolydata = src.GetOutput()
-
- values = list(zip(xvalues, yvalues))
- zs = [[0.0]] * len(values)
- values = np.append(values, zs, axis=1)
-
- pointsPolydata.GetPoints().SetData(numpy_to_vtk(values, deep=True))
- cloud = Actor(pointsPolydata)
-
- col = None
- if c is not None:
- col = colors.getColor(c)
-
- r = 0.47 / n * 1.2 * dx
-
- hexs, binmax = [], 0
- for i in range(n + 3):
- for j in range(m + 2):
- cyl = vtk.vtkCylinderSource()
- cyl.SetResolution(6)
- cyl.CappingOn()
- cyl.SetRadius(0.5)
- cyl.SetHeight(0.1)
- cyl.Update()
- t = vtk.vtkTransform()
- if not i % 2:
- p = (i / 1.33, j / 1.12, 0)
- else:
- p = (i / 1.33, j / 1.12 + 0.443, 0)
- q = (p[0] / n * 1.2 * dx + xmin, p[1] / m * dy + ymin, 0)
- ids = cloud.closestPoint(q, radius=r, returnIds=True)
- ne = len(ids)
- if fill:
- t.Translate(p[0], p[1], ne / 2)
- t.Scale(1, 1, ne * 10)
- else:
- t.Translate(p[0], p[1], ne)
- t.RotateX(90) # put it along Z
- tf = vtk.vtkTransformPolyDataFilter()
- tf.SetInputData(cyl.GetOutput())
- tf.SetTransform(t)
- tf.Update()
- if c is None:
- col=i
- h = Actor(tf.GetOutput(), c=col, alpha=alpha)
- h.flat()
- h.GetProperty().SetSpecular(0)
- h.GetProperty().SetDiffuse(1)
- h.PickableOff()
- hexs.append(h)
- if ne > binmax:
- binmax = ne
-
- asse = Assembly(hexs)
- asse.SetScale(1 / n * 1.2 * dx, 1 / m * dy, norm / binmax * (dx + dy) / 4)
- asse.SetPosition(xmin, ymin, 0)
- return asse
-
-
def delaunay2D(plist, mode='xy', tol=None):
"""
Create a mesh from points in the XY plane.
@@ -831,7 +1275,7 @@ def smoothMLS1D(actor, f=0.2, radius=None, showNLines=0):
newline.append(newp)
if showNLines and not i % ndiv:
- fline = fitLine(points, lw=4) # fitting plane
+ fline = fitLine(points) # fitting plane
iapts = shapes.Points(points) # blue points
acts += [fline, iapts]
diff --git a/vtkplotter/animation.py b/vtkplotter/animation.py
index 4cf98f64..2cf23afc 100644
--- a/vtkplotter/animation.py
+++ b/vtkplotter/animation.py
@@ -3,19 +3,23 @@
import vtkplotter.utils as utils
from vtkplotter.utils import ProgressBar, linInterpolate
from vtkplotter.colors import printc, getColor
+import vtkplotter.docs as docs
from vtkplotter.vtkio import Video
from vtkplotter.plotter import Plotter
__doc__ = (
"""
-Animation module which allows to animate simultaneously various objects
+Submodule which allows to animate simultaneously various objects
by specifying event times and durations of different visual effects.
See examples
`here `_.
+|animation1| |animation2|
+
N.B.: this is still an experimental feature at the moment.
"""
+ + docs._defs
)
__all__ = ['Animation']
@@ -350,7 +354,7 @@ def scale(self, acts=None, factor=1, t=None, duration=None):
def meshErode(self, act=None, corner=6, t=None, duration=None):
- """Erode a mesh by removing cells that are close to one of the 6 corners
+ """Erode a mesh by removing cells that are close to one of the 8 corners
of the bounding box.
"""
if self.bookingMode:
diff --git a/vtkplotter/backends.py b/vtkplotter/backends.py
index b4e0557f..79535cbe 100644
--- a/vtkplotter/backends.py
+++ b/vtkplotter/backends.py
@@ -23,8 +23,12 @@ def getNotebookBackend(actors2show, zoom, viewup):
if 'itk' in settings.notebookBackend:
from itkwidgets import view
- if vp.shape[0] != 1 or vp.shape[1] != 1:
+ if sum(vp.shape) != 2:
colors.printc("Warning: multirendering is not supported in jupyter.", c=1)
+
+# settings.notebook_plotter = view(actors=actors2show,
+# cmap='jet', ui_collapsed=True,
+# gradient_opacity=False)
polys2show = []
points2show = []
diff --git a/vtkplotter/data/box_with_dent.xml.gz b/vtkplotter/data/box_with_dent.xml.gz
new file mode 100644
index 00000000..3b2011d4
Binary files /dev/null and b/vtkplotter/data/box_with_dent.xml.gz differ
diff --git a/vtkplotter/docs.py b/vtkplotter/docs.py
index 4d68bfc1..73390b17 100644
--- a/vtkplotter/docs.py
+++ b/vtkplotter/docs.py
@@ -355,11 +355,11 @@ def tips():
:target: fxy.py_
:alt: fxy.py
-.. |histo2D.py| replace:: histo2D.py
-.. _histo2D.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/histo2D.py
-.. |histo2D| image:: https://user-images.githubusercontent.com/32848391/50738861-bfccf800-11d8-11e9-9698-c0b9dccdba4d.jpg
+.. |histoHexagonal.py| replace:: histoHexagonal.py
+.. _histoHexagonal.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/histoHexagonal.py
+.. |histoHexagonal| image:: https://user-images.githubusercontent.com/32848391/50738861-bfccf800-11d8-11e9-9698-c0b9dccdba4d.jpg
:width: 250 px
- :alt: histo2D.py
+ :alt: histoHexagonal.py
.. |align3.py| replace:: align3.py
.. _align3.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/align3.py
@@ -1130,5 +1130,39 @@ def tips():
.. |wikiphong| image:: https://upload.wikimedia.org/wikipedia/commons/6/6b/Phong_components_version_4.png
+.. |animation1.py| replace:: animation1.py
+.. _animation1.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/animation1.py
+.. |animation1| image:: https://user-images.githubusercontent.com/32848391/64273764-4b528080-cf42-11e9-90aa-2d88df239871.gif
+ :width: 300 px
+ :target: animation1.py_
+ :alt: animation1.py
+
+.. |animation2.py| replace:: animation2.py
+.. _animation2.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/other/animation2.py
+.. |animation2| image:: https://user-images.githubusercontent.com/32848391/64273191-1a258080-cf41-11e9-8a18-f192f05f11a9.gif
+ :width: 300 px
+ :target: animation2.py_
+ :alt: animation2.py
+
+.. |histoPolar.py| replace:: histoPolar.py
+.. _histoPolar.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/histoPolar.py
+.. |histoPolar| image:: https://user-images.githubusercontent.com/32848391/64912717-5754f400-d733-11e9-8a1f-612165955f23.png
+ :width: 250 px
+ :target: histoPolar.py_
+ :alt: histoPolar.py
+
+.. |polarPlot.py| replace:: polarPlot.py
+.. _polarPlot.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/polarPlot.py
+.. |polarPlot| image:: https://user-images.githubusercontent.com/32848391/64992590-7fc82400-d8d4-11e9-9c10-795f4756a73f.png
+ :width: 250 px
+ :target: polarPlot.py_
+ :alt: polarPlot.py
+
+.. |donutPlot.py| replace:: donutPlot.py
+.. _donutPlot.py: https://github.com/marcomusy/vtkplotter/blob/master/examples/basic/donutPlot.py
+.. |donutPlot| image:: https://user-images.githubusercontent.com/32848391/64998178-6f6b7580-d8e3-11e9-9bd8-8dfb9ccd90e4.png
+ :width: 250 px
+ :target: donutPlot.py_
+ :alt: donutPlot.py
"""
diff --git a/vtkplotter/dolfin.py b/vtkplotter/dolfin.py
index ecff12fa..9cfb4ac8 100644
--- a/vtkplotter/dolfin.py
+++ b/vtkplotter/dolfin.py
@@ -622,14 +622,16 @@ def plot(*inputobj, **options):
arrs = MeshArrows(u, scale=scale)
else:
arrs = MeshLines(u, scale=scale)
- if legend and not 'mesh' in mode:
- arrs.legend(legend)
- if c:
- arrs.color(c)
- arrs.color(c)
- if alpha:
- arrs.alpha(alpha)
- actors.append(arrs)
+
+ if arrs:
+ if legend and not 'mesh' in mode:
+ arrs.legend(legend)
+ if c:
+ arrs.color(c)
+ arrs.color(c)
+ if alpha:
+ arrs.alpha(alpha)
+ actors.append(arrs)
#################################################################
diff --git a/vtkplotter/plotter.py b/vtkplotter/plotter.py
index 6501ed5a..de5aa655 100644
--- a/vtkplotter/plotter.py
+++ b/vtkplotter/plotter.py
@@ -438,7 +438,6 @@ def __init__(
self.clickedActor = None # holds the actor that has been clicked
self.renderer = None # current renderer
self.renderers = [] # list of renderers
- self.shape = shape
self.pos = pos
self.interactive = interactive # allows to interact with renderer
self.axes = axes # show axes type nr.
@@ -535,67 +534,115 @@ def __init__(
minl = l
shape = lm[ind]
- if size == "auto": # figure out a reasonable window size
- f = 1.5
- xs = y / f * shape[1] # because y x / f: # shrink
- xs = x / f
- ys = xs / shape[1] * shape[0]
- if ys > y / f:
- ys = y / f
- xs = ys / shape[0] * shape[1]
- self.size = (int(xs), int(ys))
- if shape == (1, 1):
- self.size = (int(y / f), int(y / f)) # because y 3:
- self.legendSize *= 2
-
- image_actor=None
- bgname = str(self.backgrcol).lower()
- if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname:
- self.window.SetNumberOfLayers(2)
- self.backgroundRenderer = vtk.vtkRenderer()
- self.backgroundRenderer.SetLayer(0)
- self.backgroundRenderer.InteractiveOff()
- self.backgroundRenderer.SetBackground(colors.getColor(bg2))
- image_actor = Picture(self.backgrcol)
- self.window.AddRenderer(self.backgroundRenderer)
- self.backgroundRenderer.AddActor(image_actor)
-
- for i in reversed(range(shape[0])):
- for j in range(shape[1]):
- if settings.useOpenVR:
- arenderer = vtk.vtkOpenVRRenderer()
+ if isinstance(shape, str):
+
+ if size == "auto":
+ if '|' in shape:
+ self.size = (800, 1200)
+ n = int(shape.split('|')[0])
+ m = int(shape.split('|')[1])
+ rangen = reversed(range(n))
+ rangem = reversed(range(m))
else:
- arenderer = vtk.vtkRenderer()
- arenderer.SetUseHiddenLineRemoval(settings.hiddenLineRemoval)
- arenderer.SetLightFollowCamera(settings.lightFollowsCamera)
- arenderer.SetUseFXAA(settings.useFXAA)
- arenderer.SetUseDepthPeeling(settings.useDepthPeeling)
-
- if image_actor:
- arenderer.SetLayer(1)
-
+ self.size = (1200, 800)
+ m = int(shape.split('/')[0])
+ n = int(shape.split('/')[1])
+ rangen = range(n)
+ rangem = range(m)
+
+ if n>=m:
+ xsplit = m/(n+m)
+ else:
+ xsplit = 1-n/(n+m)
+ if settings.multiRenderingSplittingPosition:
+ xsplit = settings.multiRenderingSplittingPosition
+
+ for i in rangen:
+ arenderer = vtk.vtkRenderer()
+ arenderer.SetUseHiddenLineRemoval(settings.hiddenLineRemoval)
+ arenderer.SetLightFollowCamera(settings.lightFollowsCamera)
+ arenderer.SetUseFXAA(settings.useFXAA)
+ arenderer.SetUseDepthPeeling(settings.useDepthPeeling)
arenderer.SetBackground(colors.getColor(self.backgrcol))
- if bg2:
- arenderer.GradientBackgroundOn()
- arenderer.SetBackground2(colors.getColor(bg2))
-
- x0 = i / shape[0]
- y0 = j / shape[1]
- x1 = (i + 1) / shape[0]
- y1 = (j + 1) / shape[1]
- arenderer.SetViewport(y0, x0, y1, x1)
+ if '|' in shape: arenderer.SetViewport(0, i/n, xsplit, (i+1)/n)
+ if '/' in shape: arenderer.SetViewport(i/n, 0, (i+1)/n, xsplit )
self.renderers.append(arenderer)
self.axes_instances.append(None)
+ for i in rangem:
+ arenderer = vtk.vtkRenderer()
+ arenderer.SetUseHiddenLineRemoval(settings.hiddenLineRemoval)
+ arenderer.SetLightFollowCamera(settings.lightFollowsCamera)
+ arenderer.SetUseFXAA(settings.useFXAA)
+ arenderer.SetUseDepthPeeling(settings.useDepthPeeling)
+ arenderer.SetBackground(colors.getColor(self.backgrcol))
+ if '|' in shape: arenderer.SetViewport(xsplit, i/m, 1, (i+1)/m)
+ if '/' in shape: arenderer.SetViewport(i/m, xsplit, (i+1)/m, 1)
+ self.renderers.append(arenderer)
+ self.axes_instances.append(None)
+
+ else:
+
+ if size == "auto": # figure out a reasonable window size
+ f = 1.5
+ xs = y / f * shape[1] # because y x / f: # shrink
+ xs = x / f
+ ys = xs / shape[1] * shape[0]
+ if ys > y / f:
+ ys = y / f
+ xs = ys / shape[0] * shape[1]
+ self.size = (int(xs), int(ys))
+ if shape == (1, 1):
+ self.size = (int(y / f), int(y / f)) # because y 3:
+ self.legendSize *= 2
+
+ image_actor=None
+ bgname = str(self.backgrcol).lower()
+ if ".jpg" in bgname or ".jpeg" in bgname or ".png" in bgname:
+ self.window.SetNumberOfLayers(2)
+ self.backgroundRenderer = vtk.vtkRenderer()
+ self.backgroundRenderer.SetLayer(0)
+ self.backgroundRenderer.InteractiveOff()
+ self.backgroundRenderer.SetBackground(colors.getColor(bg2))
+ image_actor = Picture(self.backgrcol)
+ self.window.AddRenderer(self.backgroundRenderer)
+ self.backgroundRenderer.AddActor(image_actor)
+
+ for i in reversed(range(shape[0])):
+ for j in range(shape[1]):
+ if settings.useOpenVR:
+ arenderer = vtk.vtkOpenVRRenderer()
+ else:
+ arenderer = vtk.vtkRenderer()
+ arenderer.SetUseHiddenLineRemoval(settings.hiddenLineRemoval)
+ arenderer.SetLightFollowCamera(settings.lightFollowsCamera)
+ arenderer.SetUseFXAA(settings.useFXAA)
+ arenderer.SetUseDepthPeeling(settings.useDepthPeeling)
+
+ if image_actor:
+ arenderer.SetLayer(1)
+
+ arenderer.SetBackground(colors.getColor(self.backgrcol))
+ if bg2:
+ arenderer.GradientBackgroundOn()
+ arenderer.SetBackground2(colors.getColor(bg2))
+
+ x0 = i / shape[0]
+ y0 = j / shape[1]
+ x1 = (i + 1) / shape[0]
+ y1 = (j + 1) / shape[1]
+ arenderer.SetViewport(y0, x0, y1, x1)
+ self.renderers.append(arenderer)
+ self.axes_instances.append(None)
if "full" in size and not offscreen: # full screen
self.window.SetFullScreen(True)
diff --git a/vtkplotter/settings.py b/vtkplotter/settings.py
index e8da19ae..6eec45ab 100644
--- a/vtkplotter/settings.py
+++ b/vtkplotter/settings.py
@@ -25,6 +25,9 @@
showRendererFrame = True
rendererFrameColor = None
+ # In multirendering mode set the position of the horizontal of vertical splitting [0,1]
+ multiRenderingSplittingPosition = None
+
# Use tex, matplotlib latex compiler
usetex = False
@@ -62,6 +65,7 @@
voro_path = '/usr/local/bin'
+
Usage example:
.. code-block:: python
@@ -132,6 +136,9 @@
# Set parallel projection On or Off (place camera to infinity, no perspective effects)
useParallelProjection = False
+# In multirendering mode set the position of the horizontal of vertical splitting [0,1]
+multiRenderingSplittingPosition = None
+
# Path to Voro++ library, http://math.lbl.gov/voro++
voro_path = '/usr/local/bin'
diff --git a/vtkplotter/shapes.py b/vtkplotter/shapes.py
index 5825f58b..b0169d99 100644
--- a/vtkplotter/shapes.py
+++ b/vtkplotter/shapes.py
@@ -19,6 +19,7 @@
"Point",
"Points",
"Line",
+ "DashedLine",
"Tube",
"Lines",
"Spline",
@@ -412,6 +413,76 @@ def Line(p0, p1=None, c="r", alpha=1, lw=1, dotted=False, res=None):
return actor
+def DashedLine(p0, p1=None, spacing=None, c="red", alpha=1, lw=1):
+ """
+ Build a dashed line segment between points `p0` and `p1`.
+ If `p0` is a list of points returns the line connecting them.
+ A 2D set of coords can also be passed as p0=[x..], p1=[y..].
+
+ :param float spacing: physical size of the dash.
+ :param c: color name, number, or list of [R,G,B] colors.
+ :type c: int, str, list
+ :param float alpha: transparency in range [0,1].
+ :param lw: line width.
+ """
+ # detect if user is passing a 2D ist of points as p0=xlist, p1=ylist:
+ if len(p0) > 3:
+ if not utils.isSequence(p0[0]) and not utils.isSequence(p1[0]) and len(p0)==len(p1):
+ # assume input is 2D xlist, ylist
+ p0 = list(zip(p0, p1))
+ p1 = None
+
+ # detect if user is passing a list of points:
+ if utils.isSequence(p0[0]):
+ listp = p0
+ else: # or just 2 points to link
+ listp = [p0, p1]
+
+ if not spacing:
+ spacing = np.linalg.norm(np.array(listp[1]) - listp[0])/50
+
+ polylns = vtk.vtkAppendPolyData()
+ for ipt in range(1, len(listp)):
+ p0 = np.array(listp[ipt-1])
+ p1 = np.array(listp[ipt])
+ v = p1-p0
+ n1 = int(np.linalg.norm(v)/spacing)
+
+ for i in range(1, n1+2):
+ if (i-1)/n1>1:
+ continue
+
+ if i%2:
+ q0 = p0 + (i-1)/n1*v
+ if i/n1>1:
+ q1 = p1
+ else:
+ q1 = p0 + i/n1*v
+ lineSource = vtk.vtkLineSource()
+ lineSource.SetPoint1(q0)
+ lineSource.SetPoint2(q1)
+ lineSource.Update()
+ polylns.AddInputData(lineSource.GetOutput())
+# elif subspacing:
+# q2 = p0 + (i+1)/n1*p1
+# w1 = (q2-q1)*(1-spacing2/spacing1)/2
+# w2 = (q2-q1)*(1+spacing2/spacing1)/2
+# lineSource = vtk.vtkLineSource()
+# lineSource.SetPoint1(w1)
+# lineSource.SetPoint2(w2)
+# lineSource.Update()
+# polylns.AddInputData(lineSource.GetOutput())
+
+ polylns.Update()
+ poly = polylns.GetOutput()
+
+ actor = Actor(poly, c, alpha).lw(lw)
+ actor.base = np.array(p0)
+ actor.top = np.array(p1)
+ settings.collectable_actors.append(actor)
+ return actor
+
+
def Lines(startPoints, endPoints=None, c=None, alpha=1, lw=1, dotted=False, scale=1):
"""
Build the line segments between two lists of points `startPoints` and `endPoints`.
diff --git a/vtkplotter/version.py b/vtkplotter/version.py
index 2eec4196..2f3d333b 100644
--- a/vtkplotter/version.py
+++ b/vtkplotter/version.py
@@ -1 +1 @@
-_version='2019.4.4'
+_version='2019.4.5'