Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to show scalars as categories with a scalar bar (and how to move labels of an axis to the other side) #381

Closed
XushanLu opened this issue Apr 29, 2021 · 25 comments

Comments

@XushanLu
Copy link
Contributor

Two issues here related to the following plot:

image

Here is the code I used to plot the figure:

import numpy as np
from vedo import TetMesh, show, screenshot, settings, Picture, buildLUT, Box, \
    Plotter

import time as tm

settings.defaultFont = 'Kanopus'
# settings.useParallelProjection = True # avoid perspective parallax

tet = TetMesh('final_mesh.1.vtk')
# Clone the TetMesh object and threshold it to get the conducotrs
conductor = tet.clone().threshold(name='cell_scalars', above=0, below=4)
cond = conductor.tomesh().lineWidth(2).lineColor([7, 15, 190])

tet.threshold(name='cell_scalars', above=0, below=15)
msh = tet.tomesh().lineWidth(2).lineColor([7, 15, 190])

# Crop the entire mesh
bounds = [503500, 505000, 6414000, 6417000, -1830, 600]
msh.crop(bounds=bounds)

# Crop the conductor mesh
bounds = [503500, 505200, 6414000, 6417000, -1830, 600]
cond.crop(bounds=bounds)

# We need to build a look up table for our color bar
lut = buildLUT([
    (0.0, 'k'),
    (1.0, 'cyan'),
    (2.0, 'skyeblue'),
    (3.0, 'dodgerblue'),
    (4.0, 'blue'),
    (5.0, 'gray'),
    (6.0, 'yellow'),
    (7.0, 'gold'),
    (9.0, 'red'),
    (11.0, 'powderblue'),
    (13.0, 'lime'),
    (15.0, 'seagreen'),
]
)

msh.cmap(lut, 'cell_scalars', on='cells')
# msh.cmap("coolwarm", 'cell_scalars', on='cells')
msh.addScalarBar3D(
    pos=(505800, 6415500, -1830),
    title='Units',
    titleSize=1.5,
    sx=100,
    sy=2000,
    # titleXOffset=,
)
# Create cmap for cond
cond.cmap(lut, 'cell_scalars', on='cells')

axes_opt = dict(
    xtitle='Easting (m)',
    ytitle='Northing (m)',
    ztitle='Elevation (m)',
    xLabelSize=0.015,
    xTitlePosition=0.65,
    yTitlePosition=0.65,
    zTitlePosition=0.65,
    axesLineWidth=4,
    # xrange=msh.xbounds(),
    yrange=msh.ybounds(),
    # zInverted=True,
    xTitleOffset=0.1,
    # zTitleOffset=0.5,
    yzShift=1,
    tipSize=0.,
    # yLabelOffset=0.5,
)

# Set the camera position
plt = Plotter()
plt.camera.SetPosition( [510267.33, 6408661.841, 5262.764] )
plt.camera.SetFocalPoint( [504361.406, 6415247.796, -650.844] )
plt.camera.SetViewUp( [-0.397, 0.39, 0.831] )
plt.camera.SetDistance( 10640.747 )
plt.camera.SetClippingRange( [6131.035, 16343.212] )

size = [3940, 2160]
show(msh, cond, axes=axes_opt, size=size, interactive=True, zoom=1.2)
screenshot('model_mesh_vedo.png')
# npimg = screenshot(returnNumpy=True)
# pic = Picture(npimg)
# pic.crop(left=0.2, right=0.2, top=0.01, bottom=0.01).write('src_obs_refinement_vedo.png')

# from wand import image

# with image.Image(filename='src_obs_refinement_vedo.png') as imag:
#     imag.trim(color=None, fuzz=0)
#     imag.save(filename='src_obs_refinement_vedo_trim.png')
  1. How to show scalar values as categories in the scalar bar. Right now only values are shown. In Paraview, I can show scalars as categories and it seems that I can probably also do that using PyVista.
  2. The 3D axes. The yz axes used to be located at the left of the figure (default) behaviour, but that causes the z-title to be not readable (reversed, something like the below picture). Then, I had to move it to the right side. However, the titles are not moving together with the axes (both the y- and z-axis titles). Also, the labels of the y-axis is located at the left of the axis and seems to be buried under the mesh. I want to simply move them to the right of the axis. So, how to do all of these changes?
    image

The vtk file used to plot the figure:
final_mesh.1.vtk.zip

Thanks in advance for any comments and suggestions!

@marcomusy
Copy link
Owner

This could be an improvement..?
You can unpack the Axes object and manipulate its elements like any other object in the scene:

from vedo import TetMesh, show, screenshot, settings, Picture, buildLUT, Box, Plotter, Axes

settings.defaultFont = 'Kanopus'

tet = TetMesh('data/chddem/final_mesh.1.vtk')

# Clone the TetMesh object and threshold it to get the conducotrs
conductor = tet.clone().threshold(name='cell_scalars', above=0, below=4)
cond = conductor.tomesh()#.lineWidth(1).lineColor([7, 15, 190])

tet.threshold(name='cell_scalars', above=0, below=15)
msh = tet.tomesh()#.lineWidth(1).lineColor([7, 15, 190])

# Crop the entire mesh
bounds = [503500, 505000, 6414000, 6417000, -1830, 600]
msh.crop(bounds=bounds)

# Crop the conductor mesh
bounds = [503500, 505200, 6414000, 6417000, -1830, 600]
cond.crop(bounds=bounds)

# We need to build a look up table for our color bar
lut = buildLUT([
    (0.0, 'k'),
    (1.0, 'cyan'),
    (2.0, 'skyeblue'),
    (3.0, 'dodgerblue'),
    (4.0, 'blue'),
    (5.0, 'gray'),
    (6.0, 'yellow'),
    (7.0, 'gold'),
    (9.0, 'red'),
    (11.0, 'powderblue'),
    (13.0, 'lime'),
    (15.0, 'seagreen'),
    ]
)

msh.cmap(lut, 'cell_scalars', on='cells')
msh.addScalarBar3D(
    pos=(505800, 6415500, -1830),
    title='Units',
    titleSize=1.5,
    titleXOffset=1.8,
    sx=100,
    sy=2000,
)

# Create cmap for cond
cond.cmap(lut, 'cell_scalars', on='cells')
group = msh+cond
axes = Axes(group,
            xtitle='Easting (m)',
            ytitle='Northing (m)',
            ztitle='Elevation (m)',
            xTitlePosition=0.65,
            yTitlePosition=0.65,
            zTitlePosition=0.65,
            zTitleOffset=0.04,
            axesLineWidth=3,
            yTitleOffset=-1.22,
            yzShift=1,
            yLabelRotation=90,
            yLabelOffset=-1.5,
            tipSize=0,
            yzGrid=True,
)

# manually move the Z axis back in place
for a in axes.unpack():
    # print(a.name)
    if "zAxis" in a.name or "zM" in a.name or "zN" in a.name or "yzGrid" in a.name:
        xb = group.xbounds()
        a.shift(-(xb[1]-xb[0]), 0, 0)

# Set the camera position
plt = Plotter()

plt.camera.SetPosition( [510267.33, 6408661.841, 5262.764] )
plt.camera.SetFocalPoint( [504361.406, 6415247.796, -650.844] )
plt.camera.SetViewUp( [-0.397, 0.39, 0.831] )
plt.camera.SetDistance( 10640.747 )
plt.camera.SetClippingRange( [6131.035, 16343.212] )

plt.show(msh, cond, axes, size=(1565, 1352), resetcam=0, zoom=1.3)

Screenshot from 2021-04-29 11-40-05
I could probably add an extra option to move around individual axes instead of axes planes..

About the scalarbar, the categories are not implemented... but shouldn't be difficult to do it.

@XushanLu
Copy link
Contributor Author

This is great! The axes are what I want and I think showing a grid makes it even better. I am looking forward to seeing the implementation of the categories. With that, I think vedo surely does all the things that I can do using Paraview but much better and neater! Thanks very much for your always helpful reply.

@XushanLu
Copy link
Contributor Author

Hmm, it's interesting that I cannot get the axes grid showing in my code...
image

Here is the code I used and I believe it is almost identical to what you used above.

import numpy as np
from vedo import TetMesh, show, screenshot, settings, Picture, buildLUT, Box, \
    Plotter, Axes

settings.defaultFont = 'Kanopus'
# settings.useParallelProjection = True # avoid perspective parallax

tet = TetMesh('final_mesh.1.vtk')
# Clone the TetMesh object and threshold it to get the conducotrs
conductor = tet.clone().threshold(name='cell_scalars', above=0, below=4)
cond = conductor.tomesh().lineWidth(2).lineColor('w')

tet.threshold(name='cell_scalars', above=0, below=15)
msh = tet.tomesh().lineWidth(2).lineColor('w')

# Crop the entire mesh
bounds = [503500, 505000, 6414000, 6417000, -1830, 600]
msh.crop(bounds=bounds)

# Crop the conductor mesh
bounds = [503500, 505200, 6414000, 6417000, -1830, 600]
cond.crop(bounds=bounds)

# We need to build a look up table for our color bar
lut = buildLUT([
    (0.0, 'k'),
    (1.0, 'cyan'),
    (2.0, 'skyblue'),
    (3.0, 'dodgerblue'),
    (4.0, 'blue'),
    (5.0, 'gray'),
    (6.0, 'yellow'),
    (7.0, 'gold'),
    (9.0, 'red'),
    (11.0, 'powderblue'),
    (13.0, 'lime'),
    (15.0, 'seagreen'),
],
               interpolate=False
)

msh.cmap(lut, 'cell_scalars', on='cells')
# msh.cmap("coolwarm", 'cell_scalars', on='cells')
msh.addScalarBar3D(
    pos=(505800, 6415500, -1830),
    title='Units',
    titleSize=1.5,
    sx=100,
    sy=2000,
    # titleXOffset=,
)
# Create cmap for the plot
cond.cmap(lut, 'cell_scalars', on='cells')

# Get an axes object for both msh and cond
group = msh + cond              # First combine the two into one
axes = Axes(group,
            xtitle='Easting (m)',
            ytitle='Northing (m)',
            ztitle='Elevation (m)',
            xLabelSize=0.015,
            xTitlePosition=0.65,
            yTitlePosition=0.65,
            yTitleOffset=-1.22,
            yLabelRotation=90,
            yLabelOffset=-1.5,
            zTitlePosition=0.65,
            zTitleOffset=0.04,
            axesLineWidth=3,
            yrange=msh.ybounds(),
            xTitleOffset=0.1,
            yzShift=1,
            tipSize=0.,
            yzGrid=True,
            xyGrid=True,
            )
# manually move the Z axis back in place
for a in axes.unpack():
    # print(a.name)
    if ("zAxis" in a.name or "zM" in a.name or "zN" in a.name
        or "yzGrid" in a.name):
        xb = group.xbounds()
        a.shift(-(xb[1]-xb[0]), 0, 0)

# Set the camera position
plt = Plotter()
plt.camera.SetPosition([510267.33, 6408661.841, 5262.764])
plt.camera.SetFocalPoint([504361.406, 6415247.796, -650.844])
plt.camera.SetViewUp([-0.397, 0.39, 0.831])
plt.camera.SetDistance(10640.747)
plt.camera.SetClippingRange([6131.035, 16343.212])

size = [3940, 2160]
plt.show(msh, cond, axes, size=size, interactive=True, resetcam=0, zoom=1.2)
screenshot('model_mesh_vedo.png')

I still kept the edges, although that does make the plot a bit messy. And I think the plot I got from Paraview seemingly does a better job in cropping the mesh when the 'crinkle clip' box is ticked. Below is what I got from using Praview (a terrible mess with the axes)

image

@marcomusy
Copy link
Owner

Have you by chance upgraded your mac OS?
Try:
settings.useDepthPeeling=False

the other problem is bacause you cut/crop the polygonal mesh coming from the tets faces, not the original tets.
See example examples/volumetric/tet_cutMesh2.py.

@marcomusy
Copy link
Owner

indeed cutting the tets works:

Screenshot from 2021-04-29 16-05-15

also shrinking the tets is probably giving a better visual result than drawing lines (?)

@XushanLu
Copy link
Contributor Author

I have not upgraded my OS but I am probably going to do that this weekend. Got tons of unfinished tasks opened and do not want to close them for now...
settings.useDepthPeeling=False solved the axes grid problem like a charm!

And you are right, I need to cut the TetMesh object rather than the mesh object. And here is what I got
image
I think changing the edge colors to white makes it slightly better looking. I think it would make people confused if I shrink the tets because they are expecting cells with edges for an Earth model discretized by unstructured emshes for numerical simulation purposes.

Oh, how did you get the scalar bar vertical? I tried to put add vertical=True and it complains that it is not a recognized parameter...

This is getting really cool! Thanks a bunch @marcomusy

@marcomusy
Copy link
Owner

Looks great :) you already found how to do it! I was planning to push a new vedo version later which would include a few fixes, including the vertical scalarbar.

@XushanLu
Copy link
Contributor Author

Yes. I tried really hard not to ask you for the code. I looked at that example and figured out how to do it by trial-and-error. I am going to try some vector plots later which I used Paraview Catalyst to create the original version. May need to bug you further there...

@marcomusy
Copy link
Owner

Here we are... upgrade vedo from the master then:

import vedo

vedo.settings.defaultFont = 'Kanopus'
vedo.settings.multiSamples = 8 # antialiasing

tet = vedo.TetMesh('data/chddem/final_mesh.1.vtk')
conductor = tet.clone().threshold('cell_scalars', above=0, below=4)
tet.threshold('cell_scalars', above=0, below=15)

# Crop the entire mesh
box = vedo.Box(size=[503500, 505000, 6414000, 6417000, -1830, 600])
tet.cutWithMesh(box, wholeCells=True)

# Crop the conductor mesh
box = vedo.Box(size=[503500, 505200, 6414000, 6417000, -1830, 600])
conductor.cutWithMesh(box)

# We need to build a look up table for our color bar
lut_table = [
    #value, color,   alpha, category_label
    ( 0.0, 'black',      1, "\alpha_0"),
    ( 1.0, 'cyan',       1, "\beta_1"),
    ( 2.0, 'skyblue',    1, "\Gamma_2"),
    ( 3.0, 'dodgerblue', 1, "\delta_3"),
    ( 4.0, 'blue',       1, "\epsilon_4"),
    ( 5.0, 'gray',       1, "level^5"),
    ( 6.0, 'yellow',     1, "level^6"),
    ( 7.0, 'gold',       1, "level^7"),
    ( 9.0, 'red',        1, "level^8"),
    (11.0, 'powderblue', 1, "level^9"),
    (13.0, 'lime',       1, "level^10"),
    (15.0, 'seagreen',   1, "level^11"),
]
lut = vedo.buildLUT(lut_table)

msh = tet.tomesh().lw(1).lc('white')
msh.cmap(lut, 'cell_scalars', on='cells')
msh.addScalarBar3D(
            categories=lut_table,
            pos=(505800, 6415500, -1830),
            title='Units',
            titleSize=1.25,
            sx=100,
            sy=2200,
)
# put scalarbar vertical and shift it up a bit
msh.scalarbar.rotateX(90, locally=True).rotateY(45, locally=True).shift(-300,1400,1200)

# Create cmap for cond
cond = conductor.tomesh().cmap(lut, 'cell_scalars', on='cells')

axes = vedo.Axes(msh + cond,
                xtitle='Easting (m)',
                ytitle='Northing (m)',
                ztitle='Elevation (m)',
                xTitlePosition=0.65,
                yTitlePosition=0.65,
                zTitlePosition=0.65,
                zTitleOffset=0.04,
                axesLineWidth=3,
                gridLineWidth=2,
                yTitleOffset=-0.22,
                yShiftAlongX=1,
                yLabelRotation=90,
                yLabelOffset=-1.5,
                tipSize=0,
                yzGrid=True,
                xyGridColor='k',
                xyFrameLine=True,
)

plt = vedo.Plotter(size=(1565, 1350))
plt.camera.SetPosition( [512150.624, 6410616.074, 4432.376] )
plt.camera.SetFocalPoint( [504505.154, 6415331.728, -815.481] )
plt.camera.SetViewUp( [-0.413, 0.29, 0.863] )
plt.camera.SetDistance( 10403.394 )

plt.show(msh, cond, axes, resetcam=False, zoom=1.3)

Screenshot 2021-04-29 at 22 11 26

Let me know if you have any questions. Please star the project if you find it useful and if you publish anything that leverages vedo let us know so we can add it to the list of papers!

@XushanLu
Copy link
Contributor Author

Just wonderful! I can now replace the original ugly figure that I included in my manuscript with this fancy one now. I stared the project and will send you the paper later once it is published. I will be returning the revised manuscript next week and am expecting it to be published soon (finally moderate revision this time).

I am actually preparing a presentation for our group meeting tomorrow on this very topic. I will include this fancy picture and the code used to create this in the presentation and I am pretty sure everyone is going to like this. As far as I know, almost everyone in our group needs to use Inkscape to edit figures created using Paraview simply for making better looking axes labels.

Thanks very much.

@XushanLu
Copy link
Contributor Author

May cite this as well with
M. Musy et al., "vedo, a python module for scientific analysis and visualization of 3D objects and point clouds", Zenodo, 2021, doi.org/10.5281/zenodo.4287635.

@marcomusy
Copy link
Owner

I'm glad you like it ! Keep in mind that you can reshuffle the ordering of colors and labels in scalarbar by chnging the lut_table list (by makiing a copy), if needed.
Thanks for you feedback which helped on improving the package.

@XushanLu
Copy link
Contributor Author

Yeah, I definitely like it and I had fun exploring all possibilities by learning from your code.

I have upgraded my code and the category scalar bar works perfectly for me. And guess what! The figures can now be trimmed using wand again!

But, the anti-aliasing seems to be not working by default anymore. I had to manually press Alt-A to get it working. But that is totally acceptable.

I still decided to stick to the horizontal color bar. Here is what my final thing looks like:
image
Here is what I used to have with Paraview + Inkscape:
image
At the time of making the above figure, I think I have not figured out Paraview can do the category thing and now I don't even bother to recreate that figure.

@marcomusy
Copy link
Owner

marcomusy commented Apr 30, 2021

cool!

the anti-aliasing seems to be not working by default anymore

so the line settings.multiSamples=8 has no effect?

the only other thing that looks a bit strange is that the axes grid lines are less visible than in my case, maybe because you're making the whole image bigger. In case you can play with the grid opacity and grid line width.

@XushanLu
Copy link
Contributor Author

Sorry, my bad. I forgot to include that line there. But wouldn't it be better just to make that the default behavior?

Also, increasing the gridLineWidth does make the grid lines more visible, which is good. Thanks for pointing this out. I was not even aware of that.

@marcomusy
Copy link
Owner

But wouldn't it be better just to make that the default behavior

as vtk9 is quite buggy, making it the default might not be possible. Actually can you try from command line:
vedo -r colorize_volume
try rotating a bit the scene and then press shift-A: does the volume disappear?

@XushanLu
Copy link
Contributor Author

XushanLu commented May 1, 2021

Oh, okay, I have no idea about anything related to vtk.

Tried that thing and yep the volume disappeared in no time!

@XushanLu
Copy link
Contributor Author

XushanLu commented May 1, 2021

Is there a way to control the size of the labels of a scalar bar? I think I can control the size of the title but there is nothing specifying how large is the label for the scalar bar.

@marcomusy
Copy link
Owner

Tried that thing and yep the volume disappeared in no time!

ok, that's a vtk9 bug.. it should not disappear, a added a check to disable antialiasing for volumes scenes on OSX.

Is there a way to control the size of the labels of a scalar bar?

I forgot to add it! In the master version (just pushed it) you can find addScalabar(labelSize=1)

@XushanLu
Copy link
Contributor Author

XushanLu commented May 2, 2021

I am not sure how this is supposed to work. But would I be able to get the newest code by using
pip install -U git+https://github.com/marcomusy/vedo.git

Or, should I clone the repository to my computer? Have been using svn forever and don't really know much about Git or Github in general.

@marcomusy
Copy link
Owner

yes: pip install -U git+https://github.com/marcomusy/vedo.git
is sufficient to install the dev version. I just pushed it to gihub so you need to redo the pip command above.

@XushanLu
Copy link
Contributor Author

The manuscript I mentioned earlier that uses vedo has been accepted for publish. I have finished proofreading and the journal should be able to put the final version up on their website soonish. But here is a link to the paper on the journal's website as of now: https://library.seg.org/doi/10.1190/geo2020-0657.1

I might remember to update the link after the final version appears on the website. The paper cited vedo and acknowledged your help in the end. Once again, thanks for making vedo available to us and for all the help you provided along the way. Hopefully, more people from the geophysics community could see the potential of what vedo can do in plotting all the Earth models when they read my paper.

@marcomusy
Copy link
Owner

marcomusy commented Aug 17, 2021

Congratulations for the publication!
Thanks for using vedo and for the useful feedback!

I'll add the paper info to the current list of papers leveraging the package

@Pizzanomicon
Copy link

What an amazing module!
I'm struggling a little bit right now, can the category feature be used in the 2d ScalarBar?

Additionally, is there a list of keystrokes somewhere in the documentation? The Shift-A advocacy is the first I've heard of this, but I'm very new!

Thank you!

@marcomusy
Copy link
Owner

Thanks Phil! At the moment the category feature is limited to scalarbar3d, the 2d one is in my todo list!
About keystrokes you can find them here:
https://vedo.embl.es/autodocs/content/vedo/vedo.html#command-line-interface

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants