For many applications on non-convex polyhedra, there are efficient solutions that first decompose the polyhedron into convex pieces. As an example, the Minkowski sum of two polyhedra can be computed by decomposing both polyhedra into convex pieces, compute pair-wise Minkowski sums of the convex pieces, and unite the pair-wise sums.
While it is desirable to have a decomposition into a minimum number of pieces, this problem is know to be NP-hard [Cha84]. Our implementation decomposes a Nef polyhedron into convex pieces, where is the number of edges, which have two adjacent facets that span an angle of more than 180 degrees with respect to the interior of the polyhedron. Those edges are also called reflex edges. The bound of convex pieces is worst-case optimal [Cha84].
Our decomposition runs in two steps. In the first step, each non-vertical reflex edge is resolved by insertion of vertical facets through . In the second step, we do the same with the vertical reflex edges. Figure 24.1 illustrates the two steps.
The worst-case running time of our implementation is log, where is the complexity of the polyhedron and is the number of reflex edges. But this running time is very pessimistic. Assuming that every inserted facet has complexity and that ray shooting queries run in logarithmic time, the running time is log.
At the moment our implementation is restricted to the decomposition of bounded polyhedra. An extension to unbounded polyhedra is planned.
A Nef_polyhedron_3 represents a subdivision of the three-dimensional space into vertices, edges, facets, and volumes. Each of these items contains set-selection mark, which indicates whether the item belongs to the represented point set or not. The represented polyhedron is the union of the point sets represented by the selected items. As an example, a cube is represented by 8 vertices, 12 edges, 6 facets, and 2 volumes, where one volume represents the interior of the cube and the other the volume outside of the cube. All of these items are selected, except for outer volume. Read the chapter on 3D Boolean operations on Nef polyhedra for more details 23.
The function convex_decomposition_3 takes one Nef_polyhedron_3 as input an inserts additional facets into the selected volumes. These additional facets are also selected and therefore redundant for the representation of the polyhedron. If some of these facets split a volume into two parts, each of the two volumes is represented by a separate volume item. The insertion of facets stops when all the selected volumes are convex. The modified polyhedron is the result of the function convex_decomposition_3. Note that the function convex_decomposition_3 is restricted to standard kernels. The extended kernels, which allow the representation of polyhedra with an infinite boundary (e.g. halfspaces) only add further unbounded polyhedra to the domain of representable polyhedra. Using a standard kernel, unbounded polyhedra can be identified by the selected outer volume. In such a case, we ignore the outer volume in the decomposition process.
The convex pieces of the modified polyhedron can be accessed by traversing 23.7.2, or by converting them into separate Nef polyhedra, as illustrated by the example code.
File: examples/Convex_decomposition_3/list_of_convex_parts.cpp
#include <CGAL/Exact_predicates_exact_constructions_kernel.h> #include <CGAL/Polyhedron_3.h> #include <CGAL/Nef_polyhedron_3.h> #include <CGAL/IO/Nef_polyhedron_iostream_3.h> #include <CGAL/Nef_3/SNC_indexed_items.h> #include <CGAL/convex_decomposition_3.h> #include <list> typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; typedef CGAL::Polyhedron_3<Kernel> Polyhedron_3; typedef CGAL::Nef_polyhedron_3<Kernel, CGAL::SNC_indexed_items> Nef_polyhedron_3; typedef Nef_polyhedron_3::Volume_const_iterator Volume_const_iterator; int main() { Nef_polyhedron_3 N; std::cin >> N; CGAL::convex_decomposition_3(N); std::list<Polyhedron_3> convex_parts; // the first volumes is the outer volume, which is // ignored in the decomposition Volume_const_iterator ci = ++N.volumes_begin(); for( ; ci != N.volumes_end(); ++ci) { if(ci->mark()) { Polyhedron_3 P; N.convert_inner_shell_to_polyhedron(ci->shells_begin(), P); convex_parts.push_back(P); } } std::cout << "decomposition into " << convex_parts.size() << " convex parts " << std::endl; }