"""
.. _boolean_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 ``boolean_union`` as it
simply superimposes the two meshes without performing additional
calculations on the result.
.. 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.boolean_union(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.boolean_difference(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.boolean_intersection(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 normals
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Note that these boolean filters behave differently depending on the
# orientation of the normals.
#
# Boolean difference with both cube and sphere normals pointed
# 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 normals outward, sphere inward.
cube = pv.Cube().triangulate().subdivide(3)
sphere = pv.Sphere(radius=0.6)
sphere.flip_normals()
result = cube.boolean_difference(sphere)
result.plot(color='lightblue')
# %%
# Boolean difference with cube normals inward, sphere outward.
cube = pv.Cube().triangulate().subdivide(3)
cube.flip_normals()
sphere = pv.Sphere(radius=0.6)
result = cube.boolean_difference(sphere)
result.plot(color='lightblue')
# %%
# Both cube and sphere normals inward.
cube = pv.Cube().triangulate().subdivide(3)
cube.flip_normals()
sphere = pv.Sphere(radius=0.6)
sphere.flip_normals()
result = cube.boolean_difference(sphere)
result.plot(color='lightblue')