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

Adds option to DataSetFilters.glyph to orient using vector or normal mode #6824

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

lachlangrose
Copy link

@lachlangrose lachlangrose commented Nov 20, 2024

Overview

Add the ability to choose whether to orient with vector or normal model when using the DataSetFilters.glyph method. Default is vector mode which is consistent with the existing implementation.

resolve #6823

Details

Similar to recently added color_mode, as a flag allowing the user to specify the vector mode.

@tkoyama010
Copy link
Member

pre-commit.ci autofix

pyvista/core/filters/data_set.py Show resolved Hide resolved
pyvista/core/filters/data_set.py Show resolved Hide resolved
@tkoyama010 tkoyama010 added the enhancement Changes that enhance the library label Nov 20, 2024
@tkoyama010 tkoyama010 changed the title Adds option to DataSetFilters.glyph to orient using vector or normal mode Adds option to DataSetFilters.glyph to orient using vector or normal mode Nov 20, 2024
Comment on lines +2705 to +2710
if vector_mode == 'vector':
alg.SetVectorModeToUseVector()
elif vector_mode == 'normal':
alg.SetVectorModeToUseNormal()
else:
raise ValueError(f"Invalid vector mode '{vector_mode}'")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution! Could you add some test code for this code? Other than that, it's perfect.

Copy link
Author

@lachlangrose lachlangrose Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tkoyama010 I was a little bit quick writing this fix... when I started making tests it is not as simple as I thought.

Looking at the vtk code in more detail it the SetVectorModeToUseNormal doesn't change the way the glyphs are interpreted rather it changes to using an attribute on the vtk object inNormals... See here.

What confused me is the different behaviour between how normal is interpreted as a property of a pyvista geometry class and how that normal is used by the glyph filter...

import pyvista as pv

p1 = pv.PolyData(np.array([[0, 0, 0]]))
p1["norm"] = np.array([[1, 0, 0]])
p2 = pv.PolyData(np.array([[1, 1, 1]]))
p2["norm"] = np.array([[0, 1, 0]])
p3 = pv.PolyData(np.array([[2, 2, 2]]))
p3["norm"] = np.array([[0, 0, 1]])

disk1 = pv.Disc(center=[0, 0, 0], normal=[1, 0, 0], inner=0)
disk2 = pv.Disc(center=[1, 1, 1], normal=[0, 1, 0], inner=0)
disk3 = pv.Disc(center=[2, 2, 2], normal=[0, 0, 1], inner=0)
geom = pv.Disc()
p1 = p1.glyph(
    geom=geom,
    orient="norm",
)
p2 = p2.glyph(
    geom=geom,
    orient="norm",
)
p3 = p3.glyph(
    geom=geom,
    orient="norm",
)
view = pv.Plotter()
view.add_mesh(disk1, name="disk1", color="red")
view.add_mesh(disk2, name="disk2", color="green")
view.add_mesh(disk3, name="disk3", color="blue")
view.add_mesh(p1, name="p1", color="red")
view.add_mesh(p2, name="p2", color="green")
view.add_mesh(p3, name="p3", color="blue")
view.show()

image

If we use a glyph like arrow

import pyvista as pv

p1 = pv.PolyData(np.array([[0, 0, 0]]))
p1["norm"] = np.array([[1, 0, 0]])
p2 = pv.PolyData(np.array([[1, 1, 1]]))
p2["norm"] = np.array([[0, 1, 0]])
p3 = pv.PolyData(np.array([[2, 2, 2]]))
p3["norm"] = np.array([[0, 0, 1]])

disk1 = pv.Arrow(start=[0, 0, 0], direction=[1, 0, 0], )
disk2 = pv.Arrow(start=[1, 1, 1], direction=[0, 1, 0], )
disk3 = pv.Arrow(start=[2, 2, 2], direction=[0, 0, 1], )
geom = pv.Arrow()
p1 = p1.glyph(
    geom=geom,
    orient="norm",
)
p2 = p2.glyph(
    geom=geom,
    orient="norm",
)
p3 = p3.glyph(
    geom=geom,
    orient="norm",
)
view = Loop3DView()
view.add_mesh(disk1, name="disk1", color="red")
view.add_mesh(disk2, name="disk2", color="green")
view.add_mesh(disk3, name="disk3", color="blue")
view.add_mesh(p1, name="p1", color="red")
view.add_mesh(p2, name="p2", color="green")
view.add_mesh(p3, name="p3", color="blue")
view.show()

image

I found that if I rotate the geometry by 90 degrees before passing it to the glyph method it will give me the results I expect.

import pyvista as pv

p1 = pv.PolyData(np.array([[0, 0, 0]]))
p1["norm"] = np.array([[1, 0, 0]])
p2 = pv.PolyData(np.array([[1, 1, 1]]))
p2["norm"] = np.array([[0, 1, 0]])
p3 = pv.PolyData(np.array([[2, 2, 2]]))
p3["norm"] = np.array([[0, 0, 1]])

disk1 = pv.Disc(center=[0, 0, 0], normal=[1, 0, 0], inner=0)
disk2 = pv.Disc(center=[1, 1, 1], normal=[0, 1, 0], inner=0)
disk3 = pv.Disc(center=[2, 2, 2], normal=[0, 0, 1], inner=0)
geom = pv.Disc()
geom = geom.rotate_y(90)
p1 = p1.glyph(
    geom=geom,
    orient="norm",
)
p2 = p2.glyph(
    geom=geom,
    orient="norm",
)
p3 = p3.glyph(
    geom=geom,
    orient="norm",
)
view = pv.Plotter()
view.add_mesh(disk1, name="disk1", color="red")
view.add_mesh(disk2, name="disk2", color="green")
view.add_mesh(disk3, name="disk3", color="blue")
view.add_mesh(p1, name="p1", color="red")
view.add_mesh(p2, name="p2", color="green")
view.add_mesh(p3, name="p3", color="blue")
view.add_arrows(np.array([[0,0,0],[1,1,1],[2,2,2]]),np.array([[1,0,0],[0,1,0],[0,0,1]]),color='black')

view.show()

I noticed the same issue with the rectangle glyph and parametric ellipsoid depending on the aspect ratio of the ellipsoid. I can modify this PR to just rotate the geometry by 90 degrees in the y direction if "normals" is chosen but I am not sure if it would be better to use a different name.

Copy link

codecov bot commented Nov 20, 2024

Codecov Report

Attention: Patch coverage is 40.00000% with 3 lines in your changes missing coverage. Please review.

Project coverage is 97.48%. Comparing base (8c4b5e8) to head (3733b37).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6824      +/-   ##
==========================================
- Coverage   97.49%   97.48%   -0.02%     
==========================================
  Files         143      143              
  Lines       28181    28185       +4     
==========================================
+ Hits        27476    27477       +1     
- Misses        705      708       +3     
---- 🚨 Try these New Features:

@lachlangrose
Copy link
Author

I don't think it is worthwhile adding this change anymore, unless its worth adding a parameter to rotate the initial glyph orientation. But this can just be done to the glyph before passing it to the filter. Happy to update or for it to be closed.

@MatthewFlamm
Copy link
Contributor

My understanding is that this option in the vtk filter changes the active attribute that is used to orient the object. The orient parameter in pyvista currently inherently always uses the active vector. That is, if 'orient' is a string it sets that array to be the active vectors, e.g. orient='velocity'.

You can do orient='Normals' or whatever the normals vector is named, and it should work just like the vtk filter option, but it will also set that array as the active vector. Since we aren't operating in a multistep pipeline mode for PyVista filters this is generally OK. But, adding this option would, in theory, enable running the glyph filter without resetting the active vectors and using the active normals instead. It doesn't sound like you need this functionality, and it would take some extra work in handling the difference in active normals versus active vectors to get the current PR merged.

Up to you on whether to continue on this or not. But either way, thank you for getting involved here!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Changes that enhance the library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add vector mode options to glyph filter
3 participants