I am trying to do a simple arrow in mlab to point to different areas on the graph.
Consider this example:
fig = mlab.figure( bgcolor=(0, 0, 0))
b=visual.Box(x=0, y=10, z=10)
visual.set_viewer(fig)
b=visual.Box(x=0, y=10, z=10)
b2=visual.Box(x=10,y=10,z=10, color=(0,0,1))
a=visual.Arrow(x=10,y=10,z=10, color=(1,0,0))
So in my mind the arrow should appear from the blue box, however it lives it's own misterious life and is located absolutely off. That is very strange that boxes are located and bound to the grid (so if I put b to x=10,y=10,z=10 the two boxes will be collocated), however the arrow is not. Is this a bug or a feature?
Update:
I was playing around trying to fix the above and found the behaviour of the arrow very weird. This is what I do (in ipython for interaction):
from mayavi import mlab
from tvtk.tools import visual
fig=mlab.figure( bgcolor=(0, 0, 0))
visual.set_viewer(fig)
First put the box somewhere on canvas as a reference:
b=visual.Box(x=10,y=4,z=1)
Then I want an arrow sticking out of the box:
a=visual.Arrow(x=10,y=4,z=1)
Didn't work out (same as the first part of the question):
Now, let's change length of the cone without reason
a.length_cone
returns
0.35
Let's change it
a.length_cone=0.5
Now the arrow is sticking out of the box as it should be
Change length of the cone back to 0.35 to see if this changes the arrow back to wrong position
a.length_cone=0.35
No, the arrow stays in the box where it should be. This looks like a bug.
Playing around with arrow, I wrote the following function to generate an arrow from x1,y1,z1 to x2,y2,z2
def Arrow_From_A_to_B(x1, y1, z1, x2, y2, z2):
ar1=visual.arrow(x=x1, y=y1, z=z1)
ar1.length_cone=0.4
arrow_length=np.sqrt((x2-x1)**2+(y2-y1)**2+(z2-z1)**2)
ar1.actor.scale=[arrow_length, arrow_length, arrow_length]
ar1.pos = ar1.pos/arrow_length
ar1.axis = [x2-x1, y2-y1, z2-z1]
return ar1
Seems like ArrowSource doesn't allow for arrows extending beyond a short distance. It doesn't seem very useful at all. mlab functions like quiver3d return glyphs based on PolyDataSource instead.
This will plot two boxes and an arrow between them. I'm not sure if there's a simple way to do it with quiver3d which is definitely based off of PolyDataSource but may not have the same structure somehow.
b1=visual.Box()
b2=visual.Box(x=10)
v=mlab.pipeline.vectors(mlab.pipeline.vector_scatter(0,0,0,10,0,0)) #xyzuvw
v.glyph.glyph.clamping=False
Also, the behavior you encountered with ArrowSource doesn't seem like a bug, it's more like a minor feature that wasn't really developed.
Related
I am trying to plot two surfaces which touch at exactly two points but are otherwise well separated. Depending on the viewing angle, this renders either just fine (figure 1) or it makes some mess with the top surface s2 (plasma, red) obstructing the lower one s1 (figure 2). I suppose that is due to the order in which the surfaces are plotted, so mayavi just puts one in front even though mathematically it should be in the back. How can I solve this issue? Note that I would like to have different colormaps for both surfaces, as they represent different things. Thanks a lot!
figure 1, correct plot
figure 2, wrong plot
Here the code to produce the plot. Viewing angles were chosen in the interactive window, not sure how to get the numerical values.
import numpy as np
import mayavi.mlab
x,y = np.mgrid[-np.pi:np.pi:0.01, -np.pi:np.pi:0.01]
def surface1(x,y):
return -np.sqrt((np.cos(x) + np.cos(y) - 1)**2 + np.sin(x)**2)
def surface2(x,y):
return np.sqrt((np.cos(x) + np.cos(y) - 1)**2 + np.sin(x)**2)
s1 = mayavi.mlab.surf(x,y,surface1, colormap='viridis')
s2 = mayavi.mlab.surf(x,y,surface2, colormap='plasma')
mayavi.mlab.show()
EDIT:
Finally found the issue: Need to specify the correct backend for rendering. Using ipython3 --gui=qt solves the issue. Thus the issue only appears when using the default backend (whichever that is). I wish this would be documented more clearly somewhere, would have saved me a lot of work.
I have a script that generates two plots side by side of two aspects of the same quantity. It's fairly long, so this is only the relevant (I believe) portion. The left hand plot is called by the function "plot_shearsticks2"
Depending on what data I'm plotting, the left hand plot may have slightly different dimensions, but I would like the two to have the same height always, and if the left hand one is wider, that's fine (and usually it ought to). Here is the relevant portion:
import matplotlib.pyplot as plt
figr = plt.figure(i,figsize=(11, 5))
fig=figr.add_subplot(121,aspect='equal')
figr.subplots_adjust(wspace=0.3)
shearplot = plot_shearsticks2(cat.data,[imoments[0],imoments[1],imoments[2],imoments[3],imoments[4]],scale_factor,'-k')
fig=figr.add_subplot(122,aspect='equal')
figr = plt.figure(i,figsize=(11, 5))
And below this, there are some instructions for making the right hand plot, but nothing that seems to indicate anything about how tall it is. Tinkering with the options in add_subplot, doesn't seem to have any effect -- the left hand side is always smaller for some reason. Any idea how to make them equal heights?
I always find it easier to ise the axes funciton. For example:
import pylab as pl
pl.figure(figsize=(4,3))
pl.axes([0.2, 0.2, 0.75, 0.35 ])
pl.axes([0.2, 0.55, 0.75, 0.35 ])
This way you have the ability to control the axis properties very accurately, and even partially/fully overlay one over the other. This gives you significant advantage over the subplot function.
Ended up redoing everything with GridSpec, and specifying plot heights with gs = gridspec.GridSpec(1,2,height_ratios=[1],width_ratios=[ratioxy,1]), having defined ratioxy with ratioxy=widthx/heighty
Hello I have a tuple in python with colours that are related to squares that are drawn in the canvas by the following dictionary:
colour_mapping = {0: "red", 1: "green", 2: "blue" , 3:"purple"}
To be more specific for example a node at the tuple is:
((2, 3), (3, 3))
This means that 4 squares should be drawn this way:
blue square purple square
purple square purple square
and then their colours should be changed accordingly to the next node in my tuple
To do this I iterate the tuple and for each element I draw a new rectangle at the canvas and then I call the time.sleep() function in order to give time to the user to see the differences to the previous state.
My problem is that only the last node is rendered correctly while all the others aren't shown. Can you help me?
Here is my code so far:
self.parent.title("AlienTiles")
self.style = Style()
self.style.theme_use("default")
self.frame = Frame(self, relief=RAISED, borderwidth=1)
self.frame.pack(fill=BOTH, expand=1)
self.canvas = Canvas(self.frame)
self.canvas.pack(fill=BOTH, expand=1)
self.pack(fill=BOTH, expand=1)
for i in range(len(path)) : #the tuple is path
state = path[i].state
print state
time.sleep(1)
y_offset=10
for x in state:
start_x=40
start_y=10
i=1
x_offset=0
for y in x:
x0=(start_x*i)+x_offset
y0=(start_y*i)+y_offset
x1=x0+size
y1=y0+size
colour=colour_mapping[y]
print colour
self.canvas.create_rectangle(x0, y0, x1, y1, fill=colour)
x_offset=x_offset+size+10
y_offset=y_offset+size+10
All in all, I try to make an animation described above. Is there anything I don't think correctly or something to refresh the canvas at each loop?
The only way for the canvas to refresh is for the event loop to service "redraw" events. In your loop you're never giving the event loop a chance to update, so you don't see any changes.
The quick fix is to call self.canvas.update_idletasks, but that's just a hack and not a proper solution.
The proper way to do animation is to use the event loop to do the iterations. You do this by placing work to be done on a queue -- in this case, the idle event queue. You can place things on this queue with the after command.
What you should do is write a function that does one iteration of your animation. Essentially, take everything in your while loop and move it to a function. Then, arrange for that function to be continually be called as long as there is work to do. You can either place the call to after in that function, or have a separate function controlling the animation.
Roughly speaking, the solution looks like this:
def do_one_frame(self, ...):
# do whatever you need to draw one frame
if (there_is_more_work_to_be_done):
self.after(10, do_one_frame)
This will draw one frame of your animation, check to see if there are any new frames to be drawn, and then arranges for the next frame to be drawn in 10ms. Of course, you can set that value to whatever you want in order to control the speed of the animation.
There are working examples of this technique on this website. For example, see https://stackoverflow.com/a/25431690/7432
First, I'm a bit confused about why ((2,3)(3,3)) would get you green and blue squares. Your color coding seems to indicate they would be blue and purple, would it not?
Second, I'm not fully sure I understand the statement "and then their colours should be changed accordingly to the next node in my tuple". Does that mean at one point you are going to pass in ((2,3)(3,3)) and expect to get 4 squares, then the next time pass in ((2,3)(3,3)(1,2)) and you would expect 6 squares to be drawn in blue?
Third, what is the output of your program? It seems as though you have enough print statements where you should be able to figure out where the problem lies.
Taking a guess without fully understanding the program, I would guess the problem is with one of your for loops not iterating over the proper value, which is causing not all your squares to be drawn. My guess is the first one:
for i in range(len(path)) :
But that is really a guess since like I said, I don't fully understand what is happening. I'll do my best to help if you can answer some of my questions. Sorry I'm not more help.
Simple question, but I've tried a few things and nothing seems to work.
I want to overlay some statistics onto a 3d VTK scene, using 2D vtkTextActors. This works fine, but the text is at times difficult to see, depending on what appears behind it in the 3D scene.
For this reason, I'd like to add a 2d, semi-transparent "box" behind my text actors to provide a darker background.
Which VTK object is appropriate for this? I've tried so far:
vtkLegendBoxActor: Not what I want, but I can use this with no text to display a semi-transparent box on screen. I cannot size it directly and I get warnings about not initialising some of the content.
vtkImageData: Tried manually creating image data and adding it to the scene; I believe it was placed within the 3d scene and not used as an overlay. If that's not the case then I couldn't get it to show at all.
vtkCornerAnnotation: Scales with window size, is fixed to a corner and the background opacity cannot be set AFAIK.
vtkTextActor: Cannot set a background color or opacity
Can anyone tell me how they might achieve what I'm after in VTK?
I've found a way to do this with vtkPolyMapper2D which seems to work okay. It seems to be a very stupid way to do this. If there is something more elegant, I'm all ears.
import vtk
extents = [[0,0],[620,0],[620,220],[0,220]]
polyPoints = vtk.vtkPoints()
for x, y in extents:
polyPoints.InsertNextPoint(x, y, 0)
num_corners = len(extents)
polyCells = vtk.vtkCellArray()
polyCells.InsertNextCell(num_corners + 1)
for i in range(0, num_corners):
polyCells.InsertCellPoint(i)
polyCells.InsertCellPoint(0) ## Rejoin at the end
poly_profile = vtk.vtkPolyData()
poly_profile.SetPoints(polyPoints)
poly_profile.SetPolys(polyCells) ## Goes solid
cut_triangles = vtk.vtkTriangleFilter()
cut_triangles.SetInput(poly_profile)
_poly_mapper = vtk.vtkPolyDataMapper2D()
_poly_mapper.SetInput(poly_profile)
_poly_mapper.SetInputConnection(cut_triangles.GetOutputPort())
_actor = vtk.vtkActor2D()
_actor.SetMapper(_poly_mapper)
_actor.GetProperty().SetColor([0.1,0.1,0.1])
_actor.GetProperty().SetOpacity(0.5)
#Add to renderer as normal
just use vtktexture, vtkimagedata & add your own image as texture background to the vtkrenderer by reducing the opacity like a watermark. thats it
Is there a way to whiten out the background of the axis label so that when it crosses the axis line itself, the latter does not run through it?
For example, this script (the best I managed so far)
#!/usr/bin/python
import matplotlib.pyplot as plt
xx=[1,2,3]
yy=[2,3,4]
dy=[0.1,0.2,0.05]
fig=plt.figure()
ax=fig.add_subplot(111)
ax.errorbar(xx,yy,dy,fmt='ro-',ms=6,elinewidth=4)
ax.set_xlim([0.,3.4])
ax.set_ylim([0.,4.4])
ax.set_xlabel(r'$T/t$',fontsize=16)
ax.set_ylabel(r'$S(\mathbf{Q})L^{1+\eta}$',fontsize=16)
# position the axis labels
ax.xaxis.set_label_coords(1,0)
ax.yaxis.set_label_coords(0.1,0.93)
ax.yaxis.get_label().set_rotation('horizontal')
ax.yaxis.get_label().set_backgroundcolor('w')
#ax.yaxis.get_label().set_zorder(222) #doesn't do the trick
plt.show()
produces almost what I'm looking for, but still the y-axis runs over the label: .
By default, the left spine has a zorder of 2.5. For some reason this seems to cause problems; maybe there's something in the code which only works if they're integral? Anyway, if you add
ax.spines['left'].set_zorder(2)
or more generally
ax.spines['left'].set_zorder(ax.yaxis.get_label().get_zorder()-1)
before the show, it should work. Also, set_ylabel returns the ylab object itself, so if you use "ylab = ax.set_ylabel(stuff)" you can avoid all the ax.yaxis.get_label() calls later.
Does this link help you?
http://matplotlib.sourceforge.net/faq/howto_faq.html#automatically-make-room-for-tick-labels
You can simply shift the y-axis to the right to allows some space for the $S(\mathbf{Q})L^{1+\eta}$ mark be fully placed before the axis line.