Changing the order of axes in Mayavi - python

I am generating STL files for 3D printing and then using mlab/mayavi to display them. I would like the Z axis to remain vertical as I rotate the image. According to the mayavi documentation, this can be achieved using the following incantation:
fig = mlab.gcf()
from tvtk.api import tvtk
fig.scene.interactor.interactor_style = tvtk.InteractorStyleTerrain()
Unfortunately, as you can see from this screenshot of my app, it is not the Z axis but the Y axis that gets maintained vertical.
This is an issue because 3D printers always think of Z as the vertical axis, so I really need Z and not Y to be oriented vertically. Is there a way in which this can be achieved?

I have experienced the same problem, vertically aligned along the y-axis.
To get the scene to align vertically along the z-axis (which you want), you can simply add a scene.mlab.view() call before setting the interactor.
The scene.mlab.view() call aligns the camera correctly (with z up), before the interactor is set. I have found this solution by simply testing a bunch of stuff, I could not find this "hack" in the documentation.
New code:
fig = mlab.gcf()
from tvtk.api import tvtk
fig.scene.mlab.view(0, 90)
fig.scene.interactor.interactor_style = tvtk.InteractorStyleTerrain()

You can rename them using the Mayavi button to access the pipeline, then selecting the appropiate axes object and changing the labels.
With code, you can add the xlabel, ylabel and zlabel keyword arguments to specify them. This is specified in the axes API

Related

interactive matplotlib figure: show and then close an image on top of a graph

I am creating an interactive matplotlib figure. It is interactive in the sense that when I press a letter 'i' on the keyboard, an image is loaded into the figure. In a second step I would like to remove the image again, while I am still showing the plot. I really don't want to redraw the plot, as it takes too much time.
I am using plt.imshow(img) to display the image. So far I have not come across an equivalent that closes the image. I can only close the complete figure. Does anyone know of such a function?
PLT is tricky. In general, plt.COMMANDS apply to the most recently created object and don't offer much control over the figure, axis, plots, etc. If you label your global plt variables, it makes it more clear.
import matplotlib.pyplot as plt
X = [1,2,3,4]
Y = [1,1,3,3.5]
figure = plt.figure() #Creates the window.
axis = figure.add_subplot(1,1,1) #Creates a graphic inside the window.
axis.grid(True) #Change the axis.
plots = axis.plot(X,Y) #Put a plot in the axis.
figure.show() #Open the window.
Note, that plots is a list, since arrays, X and Y, could have generated many plots. Now, lets delete the plot while the window is open and watch it disappear, then insert the plot back into the axis.
plots[0].remove()
plots = axis.plot(X,Y)
In your case, you are working with axis.imshow() instead of axis.plot().

Circular / arc spine in custom matplotlib Axes class

So I am trying to build a class to create a radialplot, also known as
Galbraith plot (https://en.wikipedia.org/wiki/Galbraith_plot).
It's essentially a cartesian plot of standardised estimates vs associated errors. The estimates values are represented by the slope of the line that goes through the origin.
here is what it looks like.
Now I want reproduce that with matplotlib so I thought I would build a
class that inherits from Axes...
So far so good...
Now I am wondering what to do with the right spine.
I could obviously plot a line and create ticks manually in the plotting area but I would rather do it properly and use the matplotlib machinery...
I saw that there are options to create a circular spine, or even arc spine.
https://matplotlib.org/3.1.0/api/spines_api.html
Looking at the spine documentation a Spine object requires, an axes, a type and a path... I am not sure what to do with that.
If I you give a path to the Spine class, what's the point of having the arc_spine or circular_spine method...
I thought I could do something like this (self refers to an Axis instance):
x, y = self.zscale_coordinates()
verts = list(zip(x,y))
codes = [Path.LINETO for i in range(len(verts))]
codes[0] = Path.MOVETO
spine = Spine(self, "right", Path(verts, codes))
self.spines["right"] = spine
Any help would be really appreciated.
Maybe someone has already done this? I found some R or Java packages but nothing with matplotlib and I would like to include it in some python code.
Thanks!

Set mayaVI pipeline properties in Python script

I'm using mayaVI to plot a surface and a vector field in 3D, with the functions mayavi.mlab.surf and mayavi.mlab.quiver3D. These functions do not have many keyword arguments that let me modify the appearance of the surface and quivers, in comparison to the Mayavi pipeline, where I can edit things down to the most miniscule detail (for example quiver arrow head radius - see example figure below). The issue is that once I have made these changes in the mayaVI pipeline, there seems to be no way to save these settings until the next time I want to redraw the figure.
I'm in particular interested in editing Contour properties of the surface, and the Glyph Source properties of the vectors (Shaft radius, Tip radius, Tip length).
Question: Is there an easy way to save the Mayavi pipeline settings until next time, or edit them directly in my Python script (i.e. without using the UI)?
Example:
Code:
#!/usr/bin/env python
import numpy as np
from mayavi import mlab
xmax = 2.0*np.pi
x, y, z = np.mgrid[-xmax:xmax:25j, -xmax:xmax:25j, -xmax:xmax:1j]
v_x = np.sin(x)*np.cos(y)
v_y = np.cos(x)*np.sin(y)
v_z = np.zeros_like(z)
v_abs = np.sqrt(v_x**2 + v_y**2) # scalar field
surf = mlab.surf( x[:,:,0], y[:,:,0], v_abs[:,:,0], colormap='magma' )
obj_j = mlab.quiver3d( x[:,:,0], y[:,:,0], z[:,:,-1], v_x[:,:,0], v_y[:,:,0], v_z[:,:,0], mode='arrow')
mlab.show()
For example, to change the tip length of the arrows,
obj = mlab.quiver3d(..., mode='arrow')
obj.glyph.glyph_source.glyph_source.tip_length = 0.9
There doesn't seem to be any complete documentation of the mayavi pipeline, but one can guess from the GUI interface about the parameters:

How to retrieve colorbar instance from figure in matplotlib

all. I want to update the colorbar of a figure when the imagedata is changed. So something like:
img = misc.lena()
fig = plt.figure()
ax = plt.imshow(im)
plt.colorbar(ax)
newimg = img+10*np.randn(512,512)
def update_colorbar(fig,ax,newimg):
cbar = fig.axes[1]
ax.set_data(newimg)
cbar.update_normal(ax)
plt.draw()
but it seems that returned results from fig.axes() does not have the colorbar instance like I expected. I can probably just pass the colorbar instance as an argument to the update function, but I thought just passing one fig parameter may be good enough. Can anyone explain a little bit on how to retrieve the colorbar from the figure? Or why 'fig.axes()' doesn't return the AxesImage or Colobar instance but just the Axes or AxesSubplot? I think I just need more understanding of the Axes/Figure stuff.Thank you!
Sometimes it can be useful to retrieve a colorbar even if it was not held in a variable.
In this case, it is possible to retrieve the colorbar from the plot with:
# Create an example image and colourbar
img = np.arange(20).reshape(5,4)
plt.imshow(img)
plt.colorbar()
# Get the current axis
ax = plt.gca()
# Get the images on an axis
im = ax.images
# Assume colorbar was plotted last one plotted last
cb = im[-1].colorbar
# Do any actions on the colorbar object (e.g. remove it)
cb.remove()
EDIT:
or, equivalently, the one liner:
plt.gca().images[-1].colorbar.remove()
N.B.: see also comments for the use of ax.collections[-1] instead of ax.images[-1]. For me it always worked only the first way, I don't know what depends on, maybe the type of data or plot.
Now you can operate on cb as if it were stored using commands described in the colorbar API. For instance you could change xlim or call update as explained in other comments. You could remove it with cb.remove() and recreate it with plt.colorbar().
plt.draw() or show should be called after to update plot.
As the image is the mappable associated to the colorbar and can be obtained with cb.mappable.
First off, I think you're getting a bit confused between the axes (basically, the plot), the figure, the scalar mappable (the image, in this case), and the colorbar instance.
The figure is the window that the plot is in. It's the top-level container.
Each figure usually has one or more axes. These are the plots/subplots.
Colorbars are also inside the figure. Adding a colorbar creates a new axes (unless you specify otherwise) for the colorbar to be displayed in. (It can't normally be displayed in the same axes as the image, because the colorbar needs to have its own x and y limits, etc.)
Some of your confusion is due to the fact that you're mixing the state-machine interface and the OO interface. It's fine to do this, but you need to understand the OO interface.
fig.axes[1] isn't the colorbar instance. It's the axes that the colorbar is plotted in. (Also, fig.axes[1] is just the second axes in the figure. It happens to be the axes that the colorbar is in for a figure with one subplot and one colorbar, but that won't generally be the case.)
If you want to update the colorbar, you'll need to hold on to the colorbar instance that colorbar returns.
Here's an example of how you'd normally approach things:
import matplotlib.pyplot as plt
import numpy as np
data = np.random.random((10,10)) # Generate some random data to plot
fig, ax = plt.subplots() # Create a figure with a single axes.
im = ax.imshow(data) # Display the image data
cbar = fig.colorbar(im) # Add a colorbar to the figure based on the image
If you're going to use update_normal to update the colorbar, it expects a ScalarMappable (e.g. an image created by imshow, the collection that scatter creates, the ContourSet that contour creates, etc) to be passed in. (There are other ways to do it, as well. Often you just want to update the limits, rather than the whole thing.) In the case of the code above, you'd call cbar.update_normal(im).
However, you haven't created a new AxesImage, you've just changed it's data. Therefore, you probably just want to do:
cbar.set_clim(newimg.min(), newimg.max())

Setting spines in matplotlibrc

For a strange reason I cannot find the way to specify spines configuration in Python's matplotlibrc file. Any idea on how to cause matplotlib not to draw upper and right spines by default?
(source: sourceforge.net)
More about info about spines in matplotlib is here
Thank you
In order to hide the right and top spines of a subplot, you need to both set the colour of the relevant spines to 'none', as well as set the tick position to 'left' for the xtick, and 'bottom' for the ytick (in order to hide the tick marks as well as the spines).
Unfortunately, none of these are currently accessible via matplotlibrc. The parameters specified in matplotlibrc are validated, and then stored in a dict called rcParams. It is then up to the individual modules to check for a key in this dict whose value will act as their default. If they don't check it for one of their options, that option is not alterable via the rc file.
Due to the nature of the rc system, and the way that spines are written, altering the code to allow for this would not be straightforward:
Spines currently obtain their colour through the same rc parameter used to define axis colours; you cannot set it to 'none' without hiding all of your axis drawing. They are also agnostic towards whether they are top, right, left, or bottom — these are really just four separate spines stored in a dict. The individual spine objects do not know what side of the plot they compose, so you cannot just add new rc params and assign the proper one during spine initialization.
self.set_edgecolor( rcParams['axes.edgecolor'] )
(./matplotlib/lib/matplotlib/spines.py, __init__(), line 54)
If you have a large amount of existing code, such that adding the axis parameters manually to each one would be too burdensome, you could alternately use a helper function to iterate through all of the Axis objects and set the values for you.
Here's an example:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.pyplot import show
# Set up a default, sample figure.
fig = plt.figure()
x = np.linspace(-np.pi,np.pi,100)
y = 2*np.sin(x)
ax = fig.add_subplot(1,2,2)
ax.plot(x,y)
ax.set_title('Normal Spines')
def hide_spines():
"""Hides the top and rightmost axis spines from view for all active
figures and their respective axes."""
# Retrieve a list of all current figures.
figures = [x for x in matplotlib._pylab_helpers.Gcf.get_all_fig_managers()]
for figure in figures:
# Get all Axis instances related to the figure.
for ax in figure.canvas.figure.get_axes():
# Disable spines.
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
# Disable ticks.
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
hide_spines()
show()
Just call hide_spines() before show(), and it will hide them in all of the figures that show() displays. I cannot think of a simpler way to alter a large number of figures, outside of spending the time to patch matplotlib and add in rc support for the needed options.
To make matplotlib not to draw upper and right spines, one can set the following in matplotlibrc file
axes.spines.right : False
axes.spines.top : False
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)

Categories