pyvista / pyvista-support

[moved] Please head over to the Discussions tab of the PyVista repository
https://github.com/pyvista/pyvista/discussions
60 stars 4 forks source link

Array is not preserved after decimation filter #211

Open nidu opened 4 years ago

nidu commented 4 years ago

Describe the bug, what's wrong, and what you expect:

After calling decimate on a PolyData instance - elevation array is not preserved. I'd really like to keep it.


To Reproduce

mesh = pv.Sphere()
mesh['elevation'] = [1] * 842
decimated = mesh.decimate(.5)
mesh, decimated

Output is

(PolyData (0x292384893a8)
   N Cells: 1680
   N Points:    842
   X Bounds:    -4.993e-01, 4.993e-01
   Y Bounds:    -4.965e-01, 4.965e-01
   Z Bounds:    -5.000e-01, 5.000e-01
   N Arrays:    2,
 PolyData (0x292384894c8)
   N Cells: 840
   N Points:    422
   X Bounds:    -4.993e-01, 4.993e-01
   Y Bounds:    -4.965e-01, 4.965e-01
   Z Bounds:    -4.999e-01, 4.999e-01
   N Arrays:    0)

Notice how N Arrays is 0 after decimation.

Screenshots

System Information:

  Date: Wed Jul 22 23:35:58 2020 W. Europe Daylight Time

                OS : Windows
            CPU(s) : 12
           Machine : AMD64
      Architecture : 64bit
       Environment : Jupyter
NVIDIA Corporation : GPU Vendor
GeForce GTX 1070/PCIe/SSE2 : GPU Renderer
4.5.0 NVIDIA 441.66 : GPU Version

  Python 3.7.7 (default, May  6 2020, 11:45:54) [MSC v.1916 64 bit (AMD64)]

           pyvista : 0.25.3
               vtk : 8.2.0
             numpy : 1.18.5
           imageio : 2.9.0
           appdirs : 1.4.3
            scooby : 0.5.6
            meshio : 4.0.16
        matplotlib : 3.2.2
             PyQt5 : 5.12.3
           IPython : 7.16.1
github-actions[bot] commented 4 years ago

Hi and welcome! Thanks for posting your first issue in the PyVista project! Someone from @pyvista/developers will chime in before too long. If your question is support related, it may be automatically transferred to https://github.com/pyvista/pyvista-support

banesullivan commented 4 years ago

The array cannot be maintained/passed through the decimate filter as the topology of the mesh is dramatically altered in this filter. We also will not add this feature as it is important for the user to realize how drastically the decimate filter affects their mesh.

In the case where you might have custom data arrays that you'd like to preserve them across this filter, you'll have to do something like resampling via the sample or interpolate filters which are seeing a big change in https://github.com/pyvista/pyvista/pull/842

Here is an example where we really demonstrate how the original mesh can change and thus any field calculated on the original mesh wouldn't be valid on the decimated mesh:

import pyvista as pv
from pyvista import examples
import numpy as np

mesh = examples.download_face()

# compute some data based on the mesh
qual = mesh.compute_cell_quality()

# decimate and recompute the data on the new mesh
target_reduction = 0.7
decimated = qual.decimate(target_reduction).compute_cell_quality()

# Define a camera potion the shows this mesh properly
cpos = [(0.4, -0.07, -0.31), (0.05, -0.13, -0.06), (-0.1, 1, 0.08)]
dargs = dict(show_edges=True, scalars='CellQuality')

# Compare the two fields and note that an interpolation wouldn't work in this case
p = pv.Plotter(shape=(1,2))
p.add_mesh(qual, **dargs)
p.subplot(0,1)
p.add_mesh(decimated, **dargs)
p.link_views()
p.show(cpos=cpos)

download

If you really want to do this and don't care about the loss of integrity in your data due to the underlying mesh drastically changing, then using the sample filter to resample the values onto the new mesh is an option (but it's not great):

mesh['lost_integrity'] = np.arange(mesh.n_points)

decimated_2 = mesh.decimate(target_reduction).sample(mesh, tolerance=0.5)

dargs = dict(show_edges=True, scalars='lost_integrity')

p = pv.Plotter(shape=(1,2))
p.add_mesh(mesh, **dargs)
p.subplot(0,1)
p.add_mesh(decimated_2, **dargs)
p.link_views()
p.show(cpos=cpos)

download


Coming soon, the interpolate filter will have much better performance. Using the changes in https://github.com/pyvista/pyvista/pull/842, the interpolate filter under a nearest neighbor approach yields:

decimated_3 = mesh.decimate(target_reduction).interpolate(mesh, n_points=2)

dargs = dict(show_edges=True, scalars='lost_integrity')

p = pv.Plotter(shape=(1,2))
p.add_mesh(mesh, **dargs)
p.subplot(0,1)
p.add_mesh(decimated_3, **dargs)
p.link_views()
p.show(cpos=cpos)

download

(note, you straight up can't do this with the current implementation of the interpolate filter and must use the new changes on the mentioned pull request)


And here is a flashier example to showcase how the new version of the interpolate filter is the bees knees 🐝

import pyvista as pv
from pyvista import examples

mesh = examples.download_st_helens().warp_by_scalar().extract_surface()

topo = mesh.triangulate().decimate(0.9).interpolate(mesh, radius=100, sharpness=10)

cpos = [(567803.5412067451, 5121265.750198333, 6779.117176048584),
 (563141.9539970291, 5116029.941437181, 1497.1660619987454),
 (-0.4346960441575112, -0.4178048067210862, 0.7977960219721372)]

# Using a colorcet colormap, be sure to pip install colorcet
dargs = dict(scalars='Elevation', cmap='fire', 
             show_edges=True, line_width=1)

p = pv.Plotter(shape=(1,2), multi_samples=8, 
               window_size=[1024*3, int(768*1.5)])
p.add_mesh(mesh, **dargs)
p.subplot(0,1)
p.add_mesh(topo, **dargs)
p.link_views()
p.show(cpos=cpos)

download

nidu commented 4 years ago

Thanks for a great explanation!

New interpolate examples look extremely good, looking forward to it! I'll see how sample works on my dataset in the meantime.

Maybe the explanation can be added to decimate documentation (or to example), because it appeared like a bug to me. Especially taking into account that decimate_pro kept the arrays, so i kept rereading function docs looking if i missed some argument.

banesullivan commented 4 years ago

Huh, decimate_pro does indeed preserve point data (but not cell data) - I did not realize that. You're absolutely right that we should document that the decimate filter will drop and point/cell arrays and then point the user to the interpolate filter. Would you be up for such a pull request, @nidu?

nidu commented 4 years ago

Yeap, i can try to come up with a PR, but that will mostly be copying your answer, because it's very detailed.