Is it possible to turn off triangles when clipping a dataset via the vtk.vtkClipDataSet
filter? The slicing filter (vtk.vtkCutter
) has an awesome GenerateTrianglesOff()
method… is there an equivalent for the clip filter?
Any tips on how to not generate triangles or tetrahedrals when clipping would be greatly appreciated… any ideas?
Here’s an example from PyVista. In this case, the input mesh (ImageData) has voxels and it gets tessellated into tetras:
import pyvista as pv
from pyvista import examples
mesh = examples.load_uniform()
mesh.plot(show_edges=True)
Then we can use the vtkClipDataSet
filter via PyVista’s .clip
routine:
clipped = mesh.clip(normal='z')
p = pv.Plotter()
p.add_mesh(clipped, show_edges=True)
p.add_mesh(mesh.outline())
p.show()
Why can’t it remain voxels like how the slice filter can preserve the topology:
slc = mesh.slice()
p = pv.Plotter()
p.add_mesh(slc, show_edges=True)
p.add_mesh(mesh.outline())
p.show()
@lassoan and I were having a discussion in Opacity issue with point picking on surface - #13 by banesullivan that makes more sense to have here
I understand that the point resolution doesn’t change but the cell resolution does - which is really important in my domain (subsurface geoscience/geophysics). Here’s an example: If you have a mesh that is full of voxel-like cells with data attributes on the cell data and you triangulate/tetrahedralize that, then there will be many more cells in the output than the original input mesh. In my domain, we use cell size to represent resolution and if you triangulate a cell then the volume of space now has more data than the original dataset which could be misleading and how much data (information) you have (you know) about that volume of space.
Here’s an example mesh: Dropbox - treemesh.vtk - Simplify your life
and some example code to demo this issue using PyVista:
import pyvista as pv
mesh = pv.read('/Users/bane/Desktop/treemesh.vtk')
subsurface = mesh.threshold(0.5, scalars='Active')
subsurface.plot(show_edges=True, color='w')
Then if run a vtkDataSetTriangleFilter
, I get this:
subsurface.triangulate().plot(show_edges=True, color='w')
or a vtkClipDataSet
filter which generates triangles, I get this:
subsurface.clip().plot(show_edges=True, color='w',)
Both of which are misleading to the actual resolution we are able to recover from our physical modeling techniques. So if I wanted to extract a region and share it with a colleage, then they would receive a misrepresentation of the resolution at which I can investigate the subsurface.
Which is why I care so much about this turning off triangles when clipping a dataset - I see no reason why it isn’t possible
Clipping is an extremely complicated operation. It is hard enough to implement it for triangles or tetrahedral elements, so I guess that’s why the mesh is converted to these elements before clipping. Maybe you could give it a try and implement clipping of cell types that are important for you in VTK. You could also add pedigree IDs to cells so that you can trace back which original cell each new cell corresponds to.
From the examples above it is not clear at all what’s happening - what VTK filters are used and how. If you would like to have VTK users or developers to look at a problem, then probably you need to provide VTK examples.
Fair. There’s a lot of other things going on like meshio and rendering (mappers/actors/etc.) and I figured I’d keep the code simple but here is exactly what is going on for the .clip()
filter which leverages the vtkClipDataSet
.
import vtk
import numpy as np
NORMALS = {
'x': [1, 0, 0],
'y': [0, 1, 0],
'z': [0, 0, 1],
'-x': [-1, 0, 0],
'-y': [0, -1, 0],
'-z': [0, 0, -1],
}
def clip(dataset, normal='x', origin=None, invert=True):
"""
Clip a dataset by a plane by specifying the origin and normal. If no
parameters are given the clip will occur in the center of that dataset
Parameters
----------
normal : tuple(float) or str
Length 3 tuple for the normal vector direction. Can also be
specified as a string conventional direction such as ``'x'`` for
``(1,0,0)`` or ``'-x'`` for ``(-1,0,0)``, etc.
origin : tuple(float)
The center ``(x,y,z)`` coordinate of the plane on which the clip
occurs
invert : bool
Flag on whether to flip/invert the clip
"""
if isinstance(normal, str):
normal = NORMALS[normal.lower()]
# find center of data if origin not specified
if origin is None:
origin = dataset.GetCenter()
# create the plane for clipping
plane = vtk.vtkPlane()
# NORMAL MUST HAVE MAGNITUDE OF 1
normal = normal / np.linalg.norm(normal)
plane.SetNormal(normal)
plane.SetOrigin(origin)
# run the clip
if isinstance(dataset, vtk.vtkPolyData):
alg = vtk.vtkClipPolyData()
elif isinstance(dataset, vtk.vtkImageData):
alg = vtk.vtkClipVolume()
alg.SetMixed3DCellGeneration(True)
else:
alg = vtk.vtkClipDataSet()
alg.SetInputDataObject(dataset) # Use the grid as the data we desire to cut
alg.SetClipFunction(plane) # the the cutter to use the plane we made
alg.SetInsideOut(invert) # invert the clip if needed
alg.Update() # Perfrom the Cut
return alg.GetOutput()
In the example above, I’m passing a vtkUnstructuredGrid
to the function
That actually might work… perhaps I could keep track of the original cell ID’s and then use those to extract the cells from the original mesh using a selection routine with vtkSelectionNode
, vtkSelection
, and vtkExtractSelection
. That parts easy… but how do I keep track of the original cell IDs when using the vtkClipDataSet
filter?
I could add the cell IDs to the cell data of the input then catch them on the output
Only issue is that the clipping plane wouldn’t be preserved in the output and I wouldn’t be able to make intersection polygons:
...
ids = np.arange(0, subsurface.n_cells, dtype=int)
subsurface['ids'] = ids
clp = subsurface.clip([-1,1,1])
clp.plot(show_edges=True, color='w', use_panel=False)
clp_preserved = subsurface.extract_cells(np.unique(clp['ids']))
clp_preserved.plot(show_edges=True, color='w', use_panel=False)
Hm… maybe I could use the slice filter and leverage the fact that vtk.vtkCutter
has an awesome GenerateTrianglesOff()
method to make those intersection polygons then merge the slice and the clipped mesh using IDs to make a hack of a solution. But I think it’ll work and could be generalized… I’ll try it!
ClipPolyData does preserve original polygons that are not clipped. Only triangles if clipped. I guess you probably know that.
See this example.
Yeah, the ClipPolyData filter is great for 2D cells - but I’m mostly clipping volumetric cells in rectilinear grids, un/structured grids, and image data so having a clear way to clip and preserve topology not intersected by the clip would be huge for me
Have you tried vtkGenericClip?
When you clip volumetric meshes, you have the option of not cutting into the cells, just keep or remove whole cells.
I just checked, GenericClip also generates triangles…
Could you share a sample dataset? I may have a solution using vtkMultiThreshold. I can extract the inside and border voxels as separate datasets. hen clip the border cells and append them with the inside.
I’m not done yet, but here is the result of the MultiThreshold.
@lorensen, that looks awesome! Here is an unstructured grid that I have been working with lately where the cell resolution is really important: https://www.dropbox.com/s/5ivldekj8weqc79/treemesh.vtk?dl=0
It’d be awesome if we could find a way to preserve cells not touched by the clipping plane and generate intersection polygons where the plane intersect cells in the mesh.
Hi,
Did you try vtkTableBasedClipDataSet ?
From documentation it says:
" It is worth mentioning that vtkTableBasedClipDataSet is capable of addressing the artifacts that may occur with vtkClipDataSet due to the possibly inconsistent triangulation modes between neighboring cells. In addition, the former is much faster than the latter. Furthermore, the former produces less cells (with ratio usually being 5~6) than by the latter in the output. In other words, this filter retains the original cells (i.e., without triangulation / tetrahedralization) wherever possible"
Vincent is correct. This example shows that triangles are NOT produced as shown in this closeup.
And, This example clips a rectilinear grid. It also reports the cell types of the clipped data.
The clipped dataset(inside) contains a
vtkUnstructuredGrid that has 14176 cells
Cell type vtkTetra occurs 3084 times.
Cell type vtkHexahedron occurs 6820 times.
Cell type vtkWedge occurs 1196 times.
Cell type vtkPyramid occurs 3076 times.
The clipped dataset(outside) contains a
vtkUnstructuredGrid that has 125600 cells
Cell type vtkTetra occurs 3276 times.
Cell type vtkHexahedron occurs 117732 times.
Cell type vtkWedge occurs 1260 times.
Cell type vtkPyramid occurs 3332 times.