bwoodsend / vtkplotlib

Wrap up VTK (python 3D graphics library) into an easy to use 3D equivalent of matplotlib
MIT License
40 stars 6 forks source link

Problem with non interactive visualization? #2

Open joseantoniobecerrapermuy opened 3 years ago

joseantoniobecerrapermuy commented 3 years ago

Hi!, I am starting to use vtkplotlib for plotting STL files in a Programming subject in the first year of a Mechanical Engineering degree because it is super-simple and nice to use!!. But non interactive visualization seems to be not working (window never opens). Wouldn't these sentences be enough?

    ...
    vpl.mesh_plot(some_mesh)
    vpl.gcf().update()
    vpl.show(block=False)
    ...

Regards, Jose.

bwoodsend commented 3 years ago

Hmm, that should open a window but not start the event loop (i.e. the window won't respond to being clicked on). So, yes those lines should be enough (and do the job on my machine). Can I have some specs?

joseantoniobecerrapermuy commented 3 years ago

Hi!,

A student told me that it successfully runs on Windows 10 with the same version of Python and using a wheel for VTK downloaded from https://download.lfd.uci.edu/pythonlibs/z4tqcw5k/VTK-9.0.1-cp39-cp39-win_amd64.whl

bwoodsend commented 3 years ago

Ahh, perfect timing. I just happen to have access to a Big Sur right now (normally I have to test macOS on a mac mini from 2008) and I can reproduce using with macOS 11.0 and Python 3.7.9 and VTK from PyPI. I also see no window although a blank icon appears on the bar at the bottom (the dock?). I also notice that, after calling show(block=True) then quiting, the window won't go away until I kill Python. Have you been seeing this too?

I tried a minimal hello world VTK example and I get the same behaviour so it looks like this issue is VTK's. (Commenting out the iren.Start() at the end is supposed to prevent it entering interactive mode.) I know that these issues aren't present on macOS 10.15 so I guess this is a Big Sur transition induced bug. Not sure there's much we can do about it...

joseantoniobecerrapermuy commented 3 years ago

I have not the problem with block=True, I mean, I don't need to kill Python in that case. If I close the window, the program continues / ends normally. I will use a virtualized Windows 10 and I will check this from time to time to see if this problem is eventually fixed. Thanks a lot for your quick answer! And thanks for vtkplotlib. It allows to do cool things with no learning curve at all! 😄

bwoodsend commented 3 years ago

I notice the PyQt5 figure behaves itself. Not only that but recent versions of PyQt5 have moved the event loop into a continuously running background thread so that it's interactive even without using the blocking show(block=True).

pip install PyQt5

Then inject the line:

vpl.QtFigure()

before any kind of vpl.plot_something() commands. You'll probably need to either call vpl.gcf().update() or click on the window to get it to redraw.

And thanks for vtkplotlib. It allows to do cool things with no learning curve at all!

You're welcome!

joseantoniobecerrapermuy commented 3 years ago

It works! I can even rotate the camera during the animation! The only colateral effect is that there is more empty background around the scene (I tried vpl.zoom_to_contents(padding=0) with no success). image Finally, is there a way of cleaning the figure between calls to show() or removing previous added meshes? (I am trying to do an animation of a crane moving a load from a ship to a truck).

bwoodsend commented 3 years ago

Oh wow! That's much cooler than what I do with vtkplotlib. I just make teeth go funny colours. vpl.zoom_to_contents(padding=0) is the correct code but I see that's broken. I know where to fix that - should be able to push a fix in a couple of days or so. In the meantime you can use the raw VTK vpl.gcf().camera.Zoom(1.5) (number > 1 means zoom in, < 1 means zoom out). Obviously, you'll need to be a bit more careful not to call it more than once.

I keep meaning to document showing/hiding/removing but still haven't. Here's something I wrote when someone else asked me the same question:

There’s several ways. You generally need to retain references to everything you wish to alter. So you may need to change your setup to something like the following if you’re not already doing this:

fig = vpl.figure() # To create a new figure.
# Or
fig = vpl.gcf() # To get the current one.

mesh = vpl.mesh_plot(...)

Once you have these references you can use any of:

# To hide it  
mesh.visible = False 
# To make it fully transparent (effectively hiding it).
mesh.opacity = 0

Or if you’re unlikely to want to reshow that mesh again then you can remove it from the figure (and save some RAM).

fig.remove_plot(mesh) # also accepts iterables of plots.
# Or by the shorthand:
fig -= mesh

# Optionally, release the `mesh` reference so Python can have its RAM
# back. 
del mesh

The figure does keep its contents in fig.plots. So if you’re feeling lazy you could just use:

fig -= fig.plots

which will remove everything. (You may need to call fig.reset_camera() after you’ve added the next mesh if it’s very different is size/position.)

joseantoniobecerrapermuy commented 3 years ago

Zoom does not seem to work properly. Maybe I am doing something wrong, I don't know. Anyway, as I can do zoom with the mouse, it is not so important. Removing objects works, great!!. Being this an exercise for students in the first year of an industrial engineering degree, it's perfect 😄. Example:

https://user-images.githubusercontent.com/5452012/104209876-84520d80-5432-11eb-8c62-02ecd9543198.mov

Again, thanks a lot for you library!

bwoodsend commented 3 years ago

That video is really cool.

I've just pushed a fix for zoom_to_contents(). Mind guinea pig-ing it?

pip install -U https://github.com/bwoodsend/vtkplotlib/archive/fix-zoom.zip

If it works, I'll merge it into the master branch and run a patch release 1.4.1 to PyPI.

joseantoniobecerrapermuy commented 3 years ago

Hi! Now, "padding" works :-) Nevertheless, I observe some things a little weird:

def paint(meshes, dock):
    """
    Paint everything.

    'meshes' include the crane and the load.
    'dock' is added outside because it doesn't change.
    """
    vpl_meshes = []
    for mesh in meshes:
        vpl_meshes.append(vpl.mesh_plot(mesh))
    vpl.zoom_to_contents(plots_to_exclude=[dock])
    figure = vpl.gcf()
    figure.update()
    figure.show(block=False)
    for mesh in vpl_meshes:
        figure.remove_plot(mesh)

https://user-images.githubusercontent.com/5452012/104616153-42bb9f80-568a-11eb-85db-e73fe144a555.mov

bwoodsend commented 3 years ago

Ughh, that flickering is not pretty. Well there's two problems here. Firstly zoom_to_contents() only zooms in. If you're going to call it repeatedly you should use vpl.reset_camera() first which will zoom out, giving zoom_to_contents() some space to work with. However this will make problem two even worse...

The second problem is that:

    vpl.zoom_to_contents(plots_to_exclude=[dock])

performs a render with dock temporarily hidden to work out how much to zoom in. I see now that this isn't a very smart thing to do! This is why the dock is flickering, because it's being hidden, rendered then reshown between every frame. I think the only fix for this is for me to think of a proper implementation for zoom_to_contents().

trinityalps commented 1 year ago

Hello, when I try to remove figures, I get an error saying "Unhashable type mesh" is there any known fix to this?

bwoodsend commented 1 year ago

You're not giving me much to go on here but I'm guessing that you're passing the original mesh instead of the output of vpl.mesh_plot() to fig.remove_plot()? i.e. something like:

mesh = Mesh.from_file("some-file.stl")
vpl.mesh_plot(mesh)
...
fig.remove_plot(mesh)

You should instead be doing:

mesh = Mesh.from_file("some-file.stl")
mesh_plot = vpl.mesh_plot(mesh)
...
fig.remove_plot(mesh_plot)