pyvista / pyvista

3D plotting and mesh analysis through a streamlined interface for the Visualization Toolkit (VTK)
https://docs.pyvista.org
MIT License
2.75k stars 508 forks source link

Add `plot_feature_edges` method #4737

Open user27182 opened 1 year ago

user27182 commented 1 year ago

Describe the feature you would like to be added.

This is somewhat related to #4736.

I propose adding a new plot_feature_edges method. Code is included below. This method could be considered a replacement for plot_boundaries.

The method plots any/all types of feature edges as desired. Each edge type is computed separately and added to a MultiBlock for plotting and legend labelling.

Links to VTK Documentation, Examples, or Class Definitions.

No response

Pseudocode or Screenshots

Sample code for this feature. The colors of the different edge types can be set with the color_cycler parameter.

import pyvista as pv

def plot_feature_edges(
        polydata,
        feature_angle=30.0,
        line_width=None,
        boundary_edges=True,
        non_manifold_edges=True,
        feature_edges=True,
        manifold_edges=True,
        color_cycler='default',
        **kwargs):

    plotter = pv.Plotter(
        off_screen=kwargs.pop('off_screen', None),
        notebook=kwargs.pop('notebook', None)
    )

    # initialize
    edges = pv.MultiBlock()
    edges.append(pv.PolyData(), name='Boundary')
    edges.append(pv.PolyData(), name='Non-manifold')
    edges.append(pv.PolyData(), name='Feature')
    edges.append(pv.PolyData(), name='Manifold')

    if boundary_edges:
        edges['Boundary'] = polydata.extract_feature_edges(
            boundary_edges=True,
            non_manifold_edges=False,
            feature_edges=False,
            manifold_edges=False)

    if non_manifold_edges:
        edges['Non-manifold'] = polydata.extract_feature_edges(
            boundary_edges=False,
            non_manifold_edges=True,
            feature_edges=False,
            manifold_edges=False)

    if feature_edges:
        edges['Feature'] = polydata.extract_feature_edges(
            feature_angle=feature_angle,
            boundary_edges=False,
            non_manifold_edges=False,
            feature_edges=True,
            manifold_edges=False)

    if manifold_edges:
        edges['Manifold'] = polydata.extract_feature_edges(
            boundary_edges=False,
            non_manifold_edges=False,
            feature_edges=False,
            manifold_edges=True)

    # plot
    plotter.add_mesh(polydata, label='Mesh', **kwargs)
    plotter.set_color_cycler(color_cycler)
    for i, edge in enumerate(edges):
        if edge.n_cells > 0:
            name = edges.get_block_name(i)
            plotter.add_mesh(
                edge, style='wireframe', label=name,
                line_width=line_width
        )

    plotter.add_legend()
    return plotter.show()

mesh = pv.ParametricRoman()
plot_feature_edges(
    mesh,
    line_width=10,
    color='k',
    style='wireframe',
    manifold_edges=False)

Sample output: image

darikg commented 1 year ago

this reminds me that I've always wished extract_feature_edges had a shortcut form so you could do something like .extract_feature_edges('boundary') and not need to set all the other type to False. Same goes here.

user27182 commented 1 year ago

What about making extract_feature_edges default to False for everything and only keep feature_edges=True? That way, extract_feature_edges, at least by default, only extracts the actual feature edges (as defined by the filter). Then we could add new methods extract_boundary_edges, extract_manifold_edges, and extract_non_manifold_edges. This would complement the existing extract_all_edges filter and I think be clearer, since each filter (by default) only extracts one type of edge.