1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
"""
.. _boolean_operations_example:
Boolean Operations
~~~~~~~~~~~~~~~~~~
Perform boolean operations with closed (manifold) surfaces.
Boolean/topological operations (intersect, union, difference) methods
are implemented for :class:`pyvista.PolyData` mesh types only and are
accessible directly from any :class:`pyvista.PolyData` mesh. Check out
:class:`pyvista.PolyDataFilters` and take a look at the following
filters:
* :func:`pyvista.PolyDataFilters.boolean_difference`
* :func:`pyvista.PolyDataFilters.boolean_union`
* :func:`pyvista.PolyDataFilters.boolean_intersection`
Essentially, boolean union, difference, and intersection are all the
same operation. Just different parts of the objects are kept at the
end.
The ``-`` operator can be used between any two :class:`pyvista.PolyData`
meshes in PyVista to cut the first mesh by the second. These meshes
must be all triangle meshes, which you can check with
:attr:`pyvista.PolyData.is_all_triangles`.
.. note::
For merging, the ``+`` operator can be used between any two meshes
in PyVista which simply calls the ``.merge()`` filter to combine
any two meshes. This is different from the operator ``|`` in PyVista
which simply calls the ``boolean_union`` filter as it simply superimposes
the two meshes without performing additional calculations on the result.
The ``&`` operator in PyVista simply calls the ``boolean_intersection``.
.. warning::
If your boolean operations don't react the way you think they
should (i.e. the wrong parts disappear), one of your meshes
probably has its normals pointing inward. Use
:func:`pyvista.PolyDataFilters.plot_normals` to visualize the normals.
"""
# sphinx_gallery_thumbnail_number = 6
from __future__ import annotations
import pyvista as pv
sphere_a = pv.Sphere()
sphere_b = pv.Sphere(center=(0.5, 0, 0))
# %%
# Boolean Union
# +++++++++++++
#
# Perform a boolean union of ``A`` and ``B`` using the
# :func:`pyvista.PolyDataFilters.boolean_union` filter.
#
# The union of two manifold meshes ``A`` and ``B`` is the mesh
# which is in ``A``, in ``B``, or in both ``A`` and ``B``.
#
# Order of operands does not matter for boolean union (the operation is
# commutative).
result = sphere_a | sphere_b
pl = pv.Plotter()
_ = pl.add_mesh(sphere_a, color='r', style='wireframe', line_width=3)
_ = pl.add_mesh(sphere_b, color='b', style='wireframe', line_width=3)
_ = pl.add_mesh(result, color='lightblue')
pl.camera_position = 'xz'
pl.show()
# %%
# Boolean Difference
# ++++++++++++++++++
#
# Perform a boolean difference of ``A`` and ``B`` using the
# :func:`pyvista.PolyDataFilters.boolean_difference` filter or the
# ``-`` operator since both meshes are :class:`pyvista.PolyData`.
#
# The difference of two manifold meshes ``A`` and ``B`` is the volume
# of the mesh in ``A`` not belonging to ``B``.
#
# Order of operands matters for boolean difference.
result = sphere_a - sphere_b
pl = pv.Plotter()
_ = pl.add_mesh(sphere_a, color='r', style='wireframe', line_width=3)
_ = pl.add_mesh(sphere_b, color='b', style='wireframe', line_width=3)
_ = pl.add_mesh(result, color='lightblue')
pl.camera_position = 'xz'
pl.show()
# %%
# Boolean Intersection
# ++++++++++++++++++++
#
# Perform a boolean intersection of ``A`` and ``B`` using the
# :func:`pyvista.PolyDataFilters.boolean_intersection` filter.
#
# The intersection of two manifold meshes ``A`` and ``B`` is the mesh
# which is the volume of ``A`` that is also in ``B``.
#
# Order of operands does not matter for boolean intersection (the
# operation is commutative).
result = sphere_a & sphere_b
pl = pv.Plotter()
_ = pl.add_mesh(sphere_a, color='r', style='wireframe', line_width=3)
_ = pl.add_mesh(sphere_b, color='b', style='wireframe', line_width=3)
_ = pl.add_mesh(result, color='lightblue')
pl.camera_position = 'xz'
pl.show()
# %%
# Behavior due to flipped faces
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Note that these boolean filters behave differently depending on the
# orientation of the faces. This is because the orientation determines
# which parts are considered to be the "outside" or the "inside" portion
# of the mesh. This example uses :meth:`~pyvista.PolyDataFilters.flip_faces`
# to flip the faces.
#
# Boolean difference with both cube and sphere faces oriented
# outward. This is the "normal" behavior.
cube = pv.Cube().triangulate().subdivide(3)
sphere = pv.Sphere(radius=0.6)
result = cube.boolean_difference(sphere)
result.plot(color='lightblue')
# %%
# Boolean difference with cube faces outward, sphere faces inward.
cube = pv.Cube().triangulate().subdivide(3)
sphere = pv.Sphere(radius=0.6).flip_faces()
result = cube.boolean_difference(sphere)
result.plot(color='lightblue')
# %%
# Boolean difference with cube faces inward, sphere faces outward.
cube = pv.Cube().triangulate().subdivide(3).flip_faces()
sphere = pv.Sphere(radius=0.6)
result = cube.boolean_difference(sphere)
result.plot(color='lightblue')
# %%
# Both cube and sphere faces inward.
cube = pv.Cube().triangulate().subdivide(3).flip_faces()
sphere = pv.Sphere(radius=0.6).flip_faces()
result = cube.boolean_difference(sphere)
result.plot(color='lightblue')
# %%
# .. tags:: filter
|