mirror of https://github.com/CGAL/cgal
Merge bf47181a90 into 26a5fc70e4
This commit is contained in:
commit
e11bdb9756
|
|
@ -6,19 +6,31 @@ namespace CGAL {
|
|||
\anchor Chapter_Convex_Decomposition_of_Polyhedra
|
||||
\anchor chapterConvexDecomposition3
|
||||
\cgalAutoToc
|
||||
\author Peter Hachenberger
|
||||
\author Peter Hachenberger, Sven Oesau
|
||||
|
||||
\section Convex_decomposition_3Introduction Introduction
|
||||
|
||||
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.
|
||||
decomposing both polyhedra into convex pieces, compute pairwise
|
||||
Minkowski sums of the convex pieces, and unite the pairwise sums.
|
||||
|
||||
While it is desirable to have a decomposition into a minimum number of
|
||||
pieces, this problem is known to be NP-hard \cgalCite{c-cpplb-84}. Our
|
||||
implementation decomposes a Nef polyhedron \f$ N\f$ into \cgalBigO{r^2} convex
|
||||
pieces, this problem is known to be NP-hard \cgalCite{c-cpplb-84}. This
|
||||
package offers two methods for decomposing polyhedra. The
|
||||
\ref Convex_decomposition_3Nef "Convex Decomposition of Nef Polyhedra"
|
||||
splits polyhedra into convex pieces with an upper bound on their number.
|
||||
The \ref Convex_decomposition_3ACD_Intro
|
||||
"Approximate Convex Decomposition" method offers a fast
|
||||
approximate decomposition of the convex hull into convex volumes. While
|
||||
any number of convex volumes can be generated, these convex volumes
|
||||
are more compact than the convex hull, but still include additional
|
||||
empty space than just the input polyhedron.
|
||||
|
||||
\section Convex_decomposition_3Nef Convex Decomposition of Nef Polyhedra
|
||||
|
||||
The method decomposes a Nef polyhedron \f$ N\f$ into \cgalBigO{r^2} convex
|
||||
pieces, where \f$ r\f$ is the number of edges that 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.
|
||||
|
|
@ -29,16 +41,16 @@ optimal \cgalCite{c-cpplb-84}.
|
|||
Vertical decomposition based on the insertion of vertical facets (viewed from the top). Left: Non-convex polyhedron. Middle: Non-vertical reflex edges have been resolved. Right: Vertical reflex edges have been resolved. The sub-volumes are convex.
|
||||
\cgalFigureEnd
|
||||
|
||||
Our decomposition runs in two steps. In the first step, each
|
||||
The decomposition runs in two steps. In the first step, each
|
||||
non-vertical reflex edge \f$ e\f$ is resolved by insertion of vertical
|
||||
facets through \f$ e\f$. In the second step, we do the same with the
|
||||
vertical reflex edges. \cgalFigureRef{figverticalDecomposition}
|
||||
facets through \f$ e\f$. In the second step, the
|
||||
vertical reflex edges are handled in the same way. \cgalFigureRef{figverticalDecomposition}
|
||||
illustrates the two steps.
|
||||
|
||||
At the moment our implementation is restricted to the decomposition of
|
||||
bounded polyhedra. An extension to unbounded polyhedra is planned.
|
||||
At the moment the implementation is restricted to the decomposition of
|
||||
bounded polyhedra.
|
||||
|
||||
\section Convex_decomposition_3InterfaceandUsage Interface and Usage
|
||||
\subsection Convex_decomposition_3InterfaceandUsage Interface and Usage
|
||||
|
||||
An instance of `Nef_polyhedron_3` represents a subdivision of the
|
||||
three-dimensional space into vertices, edges, facets, and
|
||||
|
|
@ -74,11 +86,122 @@ of accessing the convex pieces is to convert them into separate Nef
|
|||
polyhedra, as illustrated by the example code given below.
|
||||
|
||||
Note that due to the restriction to bounded polyhedra, the use of
|
||||
extended kernels is unnecessary and expensive. We therefore do not
|
||||
support the use of extended kernels in the convex decomposition.
|
||||
extended kernels is unnecessary and expensive. Therefore the use of
|
||||
extended kernels in the convex decomposition is not supported.
|
||||
|
||||
\subsection Convex_decomposition_3Example Example
|
||||
|
||||
This example shows the usage of `CGAL::convex_decomposition_3(Nef_polyhedron_3&)` to decompose a Nef polyhedron into convex parts.
|
||||
|
||||
\cgalExample{Convex_decomposition_3/list_of_convex_parts.cpp}
|
||||
|
||||
\section Convex_decomposition_3ACD_Intro Approximate Convex Decomposition
|
||||
|
||||
\cgalFigureAnchor{Acd_topfig}
|
||||
<center>
|
||||
<img src="https://soesau.github.io/acd_top.jpg" style="max-width:70%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{Acd_topfig}
|
||||
Approximate convex decomposition of the elephant.off model.\n From left to right: input mesh, 6, 9, and 12 convex volumes
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
The H-VACD method \cgalCite{cgal:mamou2016volumetric}, "Hierarchical volumetrix approximate convex decomposition", computes a set of convex volumes that fit the polyhedron. Contrary to the decomposition of the polyhedron into convex parts, the convex volumes cover the polyhedron, but also include additional volume outside of it.
|
||||
A sufficiently tight enclosure of the polyhedron by several convex volumes allows fast intersection calculation and collision detection among polyhedra while offering a non-hierachical approach and may thus be easier to use in a parallel setting.
|
||||
The resulting set of convex volumes minimizes the volume between their union and the polyhedron while fully including the input polyhedron. While the optimal solution with `n` convex hulls that cover the polydron with the smallest additional volume remains NP-hard, this method provides a fast error-driven approximation.
|
||||
|
||||
\subsection Convex_decomposition_3ACD_Algorithm Algorithm
|
||||
The algorithm computes a set of convex volumes \f$ C=\{C_i\f$ with \f$ i \in[0..n-1]\} \f$ that cover the input polyhedron while minimizing the additional covered volume:
|
||||
|
||||
\f{equation}{
|
||||
\arg \min_C d(\bigcup_{C_i \in C} C_i, P) \\
|
||||
\f}
|
||||
<center>with</center>
|
||||
\f{equation}{
|
||||
d(A, B) = |A| - |B|
|
||||
\f}
|
||||
|
||||
Where \f$|A|\f$ is the volume of A, P is the input polyhedron and \f$C_i\f$ are convex volumes. The convex volumes \f$C_i\f$ are may slightly overlap and their union contain the input polyhedron \f$ P \subset \bigcup_{C_i \in C} \f$.
|
||||
|
||||
\cgalFigureAnchor{Acd_pipelinefig}
|
||||
<center>
|
||||
<img src="https://soesau.github.io/acd_pipeline.jpg" style="max-width:90%;"/>
|
||||
</center>
|
||||
\cgalFigureCaptionBegin{Acd_pipelinefig}
|
||||
Approximate convex decomposition pipeline.\n From left to right: 1. input mesh 2. voxel grid 3. convex volumes after error-driven splitting 4. final convex volumes after merging
|
||||
\cgalFigureCaptionEnd
|
||||
|
||||
The method employs a top-down splitting phase followed by a bottom-up merging to achieve the target number of convex volumes. The splitting phase aims at decomposing the input mesh into smaller mostly convex parts. Each part of the input mesh is approximated with its convex hull. In a hierarchical manner, each part of the mesh is split into two parts when its convexity is low. The convexity is measured by the volume difference of the part and its convex hull. Splitting a part into two can be done by simply cutting the longest side of the bounding box in half. A better choice is often found by searching the longest side of the bounding box for a concave spot. However, it comes with a higher computational cost. The hierarchical splitting stops when either the convexity is sufficiently high or the maximum depth is reached.
|
||||
The volume calculation, convex hull computation and the concavity search is accelerated by a voxel grid. The grid is prepared before the splitting phase and voxel cells overlapping with triangles are labeled as surface. The remaining voxels are labeled as outside or inside by flood fill, in case the input mesh is closed, or by axis-aligned ray shooting, i.e., along x, y and z-axes in positive and negative directions. A voxel is only labeled as inside if at least 3 faces facing away from the voxel have been hit and
|
||||
no face facing towards the voxel. The convex hulls are calculated from voxel corners. Thus, a mesh with a high resolution is less penalized by its number of vertices.
|
||||
The splitting phase typically results in a number of convex volumes larger than targeted.
|
||||
|
||||
To optionally improve the fit of the convex volumes, they can be refitted to the mesh before starting the second phase. The second phase employs a bottom-up merging that reduces the number of convex volumes to the targeted number while aiming at maintaining a low volume difference between convex volumes and the input mesh. The greedy merging maintains a priority queue to incrementally merge the pair of convex volumes with the smallest increase of volume difference.
|
||||
|
||||
The splitting phase is not limited by the chosen `maximum_number_of_convex_volumes`, because a splitting into a larger number of more convex parts with a subsequent merging leads to better results.
|
||||
|
||||
|
||||
\subsection Convex_decomposition_3ACD_Parameters Parameters
|
||||
Several parameters of the algorithm impact the quality of the result as well as the running time.
|
||||
- `maximum_number_of_convex_volumes`: The maximum number of convex volumes output by the method. The actual number may be lower for mostly convex input meshes, e.g., a sphere. The impact on the running time is rather low. The default is 16.
|
||||
- `maximum_depth`: The maximum depth for the hierarchical splitting phase. For complex meshes, a higher maximum depth is required to split small concavities into convex parts. The choice of `maximum_depth` has a larger impact on the running time. The default is 10.
|
||||
- `refitting`: The convex hulls can be refitted after the splitting phase to more tightly enclose the input mesh. It increases the running time, but significantly reduces the overhead volume included by the computed convex volulmes. It is enabled by default.
|
||||
- `maximum_number_of_voxels`: This parameter controls the resolution of the voxel grid used for speed-up. Larger numbers result in a higher memory footprint and a higher running time. A small number also limits the `maximum_depth`. The voxel grid is isotropic and the longest axis of the bounding box will be split into a number of voxels equal to the cubic root of `maximum_number_of_voxels`. The default value is 1.000.000.
|
||||
- `volume_error`: The splitting of a convex volume into smaller parts is controlled by the `volume_error` which provides the tolerance for difference in volume. The difference is calculated by \f$ (|C_i| - |P_i|) / |P_i|\f$. The default value is 0.01. Thus, if a convex volume has 1 percent more volume that the part of the input mesh it approximates, it will be further divided.
|
||||
- `split_at_concavity`: The splitting can be either performed after searching a concavity on the longest side of the bounding box or simply by splitting the longest side of the bounding box in half. The default value is true, i.e., splitting at the concavity.
|
||||
|
||||
\subsection Convex_decomposition_3ACD_Performance Performance
|
||||
|
||||
<b>Here will be images and more tables to show the impact of different parameters</b>
|
||||
|
||||
The method has been evaluated on several models:
|
||||
|
||||
| Data set | Faces | Volume | Convex hull volume | Overhead |
|
||||
| :---- | ----: | ----: | -: | -: |
|
||||
| Camel | 19.536 | 0.0468 | 0.15541 | 2.32388 |
|
||||
| Elephant | 5.558 | 0.0462 | 0.12987 | 1.81087 |
|
||||
| Triceratops | 5.660 | 136.732 | 336.925 | 1.46412 |
|
||||
|
||||
|
||||
If not mentioned otherwise, all tests used a volume error of 0.01, a maximum depth of 10, 1 million voxels and split at the concavity.
|
||||
|
||||
Impact of varying the number of generated convex volumes with splitting at the concavity and refitting to the convex hull of the input mesh on volume overhead:
|
||||
|
||||
|
||||
| Data set | Split location | Refitting | 4 volumes | 6 volumes | 8 volumes | 10 volumes | 12 volumes |
|
||||
| :---- | ----: | ----: | -: | -: | -: | -: | -: |
|
||||
| Camel | Concavity | + | 0.6951 | 0.4316 | 0.3016 | 0.2173 | 0.1939 |
|
||||
| Camel | Concavity | - | 0.9932 | 0.7482 | 0.6174 | 0.5507 | 0.5261 |
|
||||
| Elephant | Concavity | + | 0.7897 | 0.6505 | 0.4973 | 0.3986 | 0.3299 |
|
||||
| Elephant | Concavity | - | 1.2140 | 1.0028 | 0.8071 | 0.7290 | 0.6870 |
|
||||
| Triceratops | Concavity | + | 0.5952 | 0.3978 | 0.3548 | 0.2385 | 0.2057 |
|
||||
| Triceratops | Concavity | - | 1.0073 | 0.7490 | 0.7035 | 0.5966 | 0.5429 |
|
||||
|
||||
|
||||
And by using the mid split:
|
||||
|
||||
|
||||
| Data set | Split location | Refitting | 4 volumes | 6 volumes | 8 volumes | 10 volumes | 12 volumes |
|
||||
| :---- | ----: | ----: | -: | -: | -: | -: | -: |
|
||||
| Camel | Mid | + | 0.9970 | 0.5762 | 0.5332 | 0.4040 | 0.2390 |
|
||||
| Camel | Mid | - | 1.0875 | 0.9280 | 0.8394 | 0.6801 | 0.5529 |
|
||||
| Elephant | Mid | + | 0.7424 | 0.5672 | 0.4207 | 0.3619 | 0.3043 |
|
||||
| Elephant | Mid | - | 1.2075 | 1.0410 | 0.8247 | 0.6950 | 0.6434 |
|
||||
| Triceratops | Mid | + | 0.7671 | 0.5096 | 0.3965 | 0.3309 | 0.2494 |
|
||||
| Triceratops | Mid | - | 1.0470 | 0.8990 | 0.6749 | 0.5803 | 0.5230 |
|
||||
|
||||
The running time for all cases in the above tables were between 1.4 and 3 seconds while being slightly lower when splitting at the concavity. Although searching the voxel grid for the concavity takes additional computational time, it is more than compensated by fewer splits.
|
||||
|
||||
\subsection Convex_decomposition_3ACD_Example Example
|
||||
|
||||
The example shows the approximate convex decomposition of the knot2.off mesh into 9 convex volumes that are saved as off files.
|
||||
|
||||
\cgalExample{Convex_decomposition_3/approximate_convex_decomposition.cpp }
|
||||
|
||||
|
||||
\section Convex_decomposition_3_history Design and Implementation History
|
||||
|
||||
This package was created by Peter Hachenberger with the `CGAL::convex_decomposition_3()` method. In 2025, it has been extended by Sven Oesau with the `CGAL::approximate_convex_decomposition()` method.
|
||||
|
||||
*/
|
||||
} /* namespace CGAL */
|
||||
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@
|
|||
PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Convex Decomposition of Polyhedra"
|
||||
EXTRACT_ALL = false
|
||||
HIDE_UNDOC_CLASSES = true
|
||||
|
||||
INPUT += ${CGAL_Surface_mesh_decomposition_INCLUDE_DIR}/CGAL/approximate_convex_decomposition.h
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@
|
|||
\cgalPkgDescriptionBegin{Convex Decomposition of Polyhedra,PkgConvexDecomposition3}
|
||||
\cgalPkgPicture{Convex_decomposition_3/fig/Convex_decomposition_3-teaser.png}
|
||||
\cgalPkgSummaryBegin
|
||||
\cgalPkgAuthor{Peter Hachenberger}
|
||||
\cgalPkgDesc{This packages provides a function for decomposing a bounded polyhedron into convex sub-polyhedra. The decomposition yields \cgalBigO{r^2} convex pieces, where \f$ r\f$ is the number of edges, whose adjacent facets form an angle of more than 180 degrees with respect to the polyhedron's interior. This bound is worst-case optimal. }
|
||||
\cgalPkgAuthor{Peter Hachenberger, Sven Oesau}
|
||||
\cgalPkgDesc{This packages provides two functions for computing a set of convex volumes that cover a bounded polyhedron. The `CGAL::convex_decomposition_3()` splits provides a decomposition into \cgalBigO{r^2} convex pieces, where \f$ r\f$ is the number of edges, whose adjacent facets form an angle of more than 180 degrees with respect to the polyhedron's interior. This bound is worst-case optimal.
|
||||
The `CGAL::approximate_convex_decomposition()` instead splits the convex hull of the polyhedron into a set of convex volumes. While these convex volumes cover additional space outside of the polyhedron, the computation is fast for any chosen number of convex volumes.}
|
||||
\cgalPkgManuals{Chapter_Convex_Decomposition_of_Polyhedra,PkgConvexDecomposition3Ref}
|
||||
\cgalPkgSummaryEnd
|
||||
\cgalPkgShortInfoBegin
|
||||
|
|
@ -20,7 +21,8 @@
|
|||
\cgalClassifedRefPages
|
||||
|
||||
\cgalCRPSection{Functions}
|
||||
- `CGAL::convex_decomposition_3(Nef_polyhedron_3& N)`
|
||||
- `CGAL::approximate_convex_decomposition(FaceGraph)`
|
||||
- `CGAL::convex_decomposition_3(NefPolyhedron_3)`
|
||||
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -5,3 +5,5 @@ Algebraic_foundations
|
|||
Circulator
|
||||
Stream_support
|
||||
Nef_3
|
||||
BGL
|
||||
Polygon_mesh_processing
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/*!
|
||||
\example Convex_decomposition_3/approximate_convex_decomposition.cpp
|
||||
\example Convex_decomposition_3/list_of_convex_parts.cpp
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -14,3 +14,12 @@ file(
|
|||
foreach(cppfile ${cppfiles})
|
||||
create_single_source_cgal_program("${cppfile}")
|
||||
endforeach()
|
||||
|
||||
find_package(TBB QUIET)
|
||||
include(CGAL_TBB_support)
|
||||
|
||||
if(TARGET CGAL::TBB_support)
|
||||
target_link_libraries(approximate_convex_decomposition PRIVATE CGAL::TBB_support)
|
||||
else()
|
||||
message(STATUS "NOTICE: Intel TBB was not found. Sequential code will be used.")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
|
||||
#include <CGAL/approximate_convex_decomposition.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
|
||||
using Point = K::Point_3;
|
||||
|
||||
using Convex_hull = std::pair<std::vector<Point>, std::vector<std::array<unsigned int, 3> > >;
|
||||
using Mesh = CGAL::Surface_mesh<Point>;
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/knot2.off");
|
||||
std::cout << filename << std::endl;
|
||||
|
||||
Mesh mesh;
|
||||
if(!PMP::IO::read_polygon_mesh(filename, mesh)) {
|
||||
std::cerr << "Invalid input." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<Convex_hull> convex_volumes;
|
||||
convex_volumes.reserve(9);
|
||||
|
||||
CGAL::approximate_convex_decomposition(mesh, std::back_inserter(convex_volumes),
|
||||
CGAL::parameters::maximum_depth(10)
|
||||
.volume_error(0.1)
|
||||
.maximum_number_of_convex_volumes(9)
|
||||
.split_at_concavity(true)
|
||||
.refitting(true)
|
||||
.maximum_number_of_voxels(1000000)
|
||||
.concurrency_tag(CGAL::Parallel_if_available_tag()));
|
||||
|
||||
for (std::size_t i = 0;i<convex_volumes.size();i++) {
|
||||
const Convex_hull& ch = convex_volumes[i];
|
||||
CGAL::IO::write_polygon_soup(std::to_string(i) + ".off", ch.first, ch.second);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ namespace CGAL {
|
|||
\ingroup PkgConvexDecomposition3Ref
|
||||
|
||||
The function `convex_decomposition_3()` inserts additional facets
|
||||
into the given `Nef_polyhedron_3` `N`, such that each bounded
|
||||
into the given Nef polyhedron` `N`, such that each bounded
|
||||
marked volume (the outer volume is unbounded) is subdivided into convex
|
||||
pieces. The modified polyhedron represents a decomposition into
|
||||
\cgalBigO{r^2} convex pieces, where \f$ r\f$ is the number of edges that have two
|
||||
|
|
@ -47,10 +47,12 @@ respect to the interior of the polyhedron.
|
|||
|
||||
The worst-case running time of our implementation is
|
||||
\cgalBigO{n^2r^4\sqrt[3]{nr^2}\log{(nr)}}, where \f$ n\f$ is the complexity of
|
||||
the polyhedron (the complexity of a `Nef_polyhedron_3` is the sum
|
||||
the polyhedron (the complexity of a Nef polyhedron is the sum
|
||||
of its `Vertices`, `Halfedges` and `SHalfedges`) and \f$ r\f$
|
||||
is the number of reflex edges.
|
||||
|
||||
@tparam NefPolyhedron_3 an object of type `Nef_polyhedron_3`.
|
||||
|
||||
\pre The polyhedron `N` is bounded. Otherwise, the outer volume is ignored.
|
||||
|
||||
\post If the polyhedron `N` is non-convex, it is modified to represent the
|
||||
|
|
@ -59,22 +61,22 @@ convex decomposition. If `N` is convex, it is not modified.
|
|||
\sa `CGAL::Nef_polyhedron_3<Traits>`
|
||||
|
||||
*/
|
||||
template<typename Nef_polyhedron>
|
||||
void convex_decomposition_3(Nef_polyhedron& N)
|
||||
template<typename NefPolyhedron_3>
|
||||
void convex_decomposition_3(NefPolyhedron_3& N)
|
||||
{
|
||||
typedef typename Nef_polyhedron::SNC_structure SNC_structure;
|
||||
typedef typename NefPolyhedron_3::SNC_structure SNC_structure;
|
||||
typedef typename SNC_structure::Halfedge_handle Halfedge_handle;
|
||||
typedef typename Nef_polyhedron::Point_3 Point_3;
|
||||
typedef typename Nef_polyhedron::Vector_3 Vector_3;
|
||||
typedef typename Nef_polyhedron::Sphere_point Sphere_point;
|
||||
typedef typename Nef_polyhedron::FT FT;
|
||||
typedef typename NefPolyhedron_3::Point_3 Point_3;
|
||||
typedef typename NefPolyhedron_3::Vector_3 Vector_3;
|
||||
typedef typename NefPolyhedron_3::Sphere_point Sphere_point;
|
||||
typedef typename NefPolyhedron_3::FT FT;
|
||||
|
||||
typedef typename CGAL::Single_wall_creator<Nef_polyhedron> Single_wall;
|
||||
typedef typename CGAL::YVertical_wall_builder<Nef_polyhedron> YVertical_wall_builder;
|
||||
typedef typename CGAL::Reflex_vertex_searcher<Nef_polyhedron> Reflex_vertex_searcher;
|
||||
typedef typename CGAL::Ray_hit_generator2<Nef_polyhedron> Ray_hit2;
|
||||
typedef typename CGAL::External_structure_builder<Nef_polyhedron> External_structure_builder;
|
||||
typedef typename CGAL::SFace_separator<Nef_polyhedron> SFace_separator;
|
||||
typedef typename CGAL::Single_wall_creator<NefPolyhedron_3> Single_wall;
|
||||
typedef typename CGAL::YVertical_wall_builder<NefPolyhedron_3> YVertical_wall_builder;
|
||||
typedef typename CGAL::Reflex_vertex_searcher<NefPolyhedron_3> Reflex_vertex_searcher;
|
||||
typedef typename CGAL::Ray_hit_generator2<NefPolyhedron_3> Ray_hit2;
|
||||
typedef typename CGAL::External_structure_builder<NefPolyhedron_3> External_structure_builder;
|
||||
typedef typename CGAL::SFace_separator<NefPolyhedron_3> SFace_separator;
|
||||
|
||||
typedef Compare_halfedges_in_reflex_edge_sorter<Halfedge_handle, std::less<Point_3> >
|
||||
Less_edges;
|
||||
|
|
@ -86,11 +88,11 @@ void convex_decomposition_3(Nef_polyhedron& N)
|
|||
typedef typename Positively_sorted_set::const_iterator Positive_reflex_edge_iterator;
|
||||
typedef typename Negatively_sorted_set::const_iterator Negative_reflex_edge_iterator;
|
||||
|
||||
typedef typename CGAL::Reflex_edge_searcher<Nef_polyhedron, Positively_sorted_set, Negatively_sorted_set>
|
||||
typedef typename CGAL::Reflex_edge_searcher<NefPolyhedron_3, Positively_sorted_set, Negatively_sorted_set>
|
||||
Reflex_edge_searcher;
|
||||
|
||||
typedef typename CGAL::Edge_sorter<Nef_polyhedron, std::less<FT>, Negatively_sorted_set> Edge_sorter;
|
||||
typedef typename CGAL::Edge_sorter<Nef_polyhedron, std::greater<FT>, Positively_sorted_set> Edge_sorter2;
|
||||
typedef typename CGAL::Edge_sorter<NefPolyhedron_3, std::less<FT>, Negatively_sorted_set> Edge_sorter;
|
||||
typedef typename CGAL::Edge_sorter<NefPolyhedron_3, std::greater<FT>, Positively_sorted_set> Edge_sorter2;
|
||||
|
||||
External_structure_builder esb;
|
||||
SFace_separator sf_sep;
|
||||
|
|
@ -176,10 +178,10 @@ void convex_decomposition_3(Nef_polyhedron& N)
|
|||
|
||||
N.delegate(esb);
|
||||
|
||||
CGAL_assertion_code(typename Nef_polyhedron::SHalfedge_const_iterator cse);
|
||||
CGAL_assertion_code(typename NefPolyhedron_3::SHalfedge_const_iterator cse);
|
||||
CGAL_assertion_code(CGAL_forall_shalfedges(cse, N)
|
||||
if(cse->incident_sface()->mark())
|
||||
CGAL_assertion(!CGAL::is_reflex_sedge_in_any_direction<Nef_polyhedron>(cse)));
|
||||
CGAL_assertion(!CGAL::is_reflex_sedge_in_any_direction<NefPolyhedron_3>(cse)));
|
||||
}
|
||||
|
||||
} //namespace CGAL
|
||||
|
|
|
|||
|
|
@ -3516,6 +3516,16 @@ pages = "207--221"
|
|||
year = {2015}
|
||||
}
|
||||
|
||||
@article{cgal:mamou2016volumetric,
|
||||
title={Volumetric hierarchical approximate convex decomposition},
|
||||
author={Mamou, Khaled and Lengyel, E and Peters, A},
|
||||
journal={Game engine gems},
|
||||
volume={3},
|
||||
pages={141--158},
|
||||
year={2016},
|
||||
publisher={AK Peters}
|
||||
}
|
||||
|
||||
% ----------------------------------------------------------------------------
|
||||
% END OF BIBFILE
|
||||
% ----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ Release date: July 2026
|
|||
- Made the `Arr_linear_traits_2` a model of the new concept.
|
||||
- Added overloads of `draw(Arrangement_on_surface_2& arr, Bbox& bbox, ...)` that enable the drawing of arrangements induced by unbounded curves.
|
||||
|
||||
### [Convex Decomposition of Polyhedra](https://doc.cgal.org/6.2/Manual/packages.html#PkgConvexDecomposition3)
|
||||
- Added the function `CGAL::approximate_convex_decomposition()` that computes a set of convex volumes that cover an input mesh.
|
||||
It performs a hierarchical splitting of the convex hull guided by an volumetric error and performs a subsequent merging of convex hulls to obtain the targeted count.
|
||||
|
||||
### [Linear Cell Complex](https://doc.cgal.org/6.2/Manual/packages.html#PkgLinearCellComplex)
|
||||
|
||||
- **API Changes**: The following import functions have been deprecated and renamed for better naming clarity and consistency:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) 2016 GeometryFactory SARL (France).
|
||||
// All rights reserved.
|
||||
//
|
||||
// This file is part of CGAL (www.cgal.org)
|
||||
//
|
||||
// $URL$
|
||||
// $Id$
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
|
||||
//
|
||||
// Author(s) : Andreas Fabri
|
||||
//
|
||||
// Warning: this file is generated, see include/CGAL/license/README.md
|
||||
|
||||
#ifndef CGAL_LICENSE_SURFACE_MESH_DECOMPOSITION_H
|
||||
#define CGAL_LICENSE_SURFACE_MESH_DECOMPOSITION_H
|
||||
|
||||
#include <CGAL/config.h>
|
||||
#include <CGAL/license.h>
|
||||
|
||||
#ifdef CGAL_SURFACE_MESH_DECOMPOSITION_COMMERCIAL_LICENSE
|
||||
|
||||
# if CGAL_SURFACE_MESH_DECOMPOSITION_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE
|
||||
|
||||
# if defined(CGAL_LICENSE_WARNING)
|
||||
|
||||
CGAL_pragma_warning("Your commercial license for CGAL does not cover "
|
||||
"this release of the Triangulated Surface Mesh Decomposition package.")
|
||||
# endif
|
||||
|
||||
# ifdef CGAL_LICENSE_ERROR
|
||||
# error "Your commercial license for CGAL does not cover this release \
|
||||
of the Triangulated Surface Mesh Decomposition package. \
|
||||
You get this error, as you defined CGAL_LICENSE_ERROR."
|
||||
# endif // CGAL_LICENSE_ERROR
|
||||
|
||||
# endif // CGAL_SURFACE_MESH_DECOMPOSITION_COMMERCIAL_LICENSE < CGAL_RELEASE_DATE
|
||||
|
||||
#else // no CGAL_SURFACE_MESH_DECOMPOSITION_COMMERCIAL_LICENSE
|
||||
|
||||
# if defined(CGAL_LICENSE_WARNING)
|
||||
CGAL_pragma_warning("\nThe macro CGAL_SURFACE_MESH_DECOMPOSITION_COMMERCIAL_LICENSE is not defined."
|
||||
"\nYou use the CGAL Triangulated Surface Mesh Decomposition package under "
|
||||
"the terms of the GPLv3+.")
|
||||
# endif // CGAL_LICENSE_WARNING
|
||||
|
||||
# ifdef CGAL_LICENSE_ERROR
|
||||
# error "The macro CGAL_SURFACE_MESH_DECOMPOSITION_COMMERCIAL_LICENSE is not defined.\
|
||||
You use the CGAL Triangulated Surface Mesh Decomposition package under the terms of \
|
||||
the GPLv3+. You get this error, as you defined CGAL_LICENSE_ERROR."
|
||||
# endif // CGAL_LICENSE_ERROR
|
||||
|
||||
#endif // no CGAL_SURFACE_MESH_DECOMPOSITION_COMMERCIAL_LICENSE
|
||||
|
||||
#endif // CGAL_LICENSE_SURFACE_MESH_DECOMPOSITION_H
|
||||
|
|
@ -93,6 +93,7 @@ Straight_skeleton_extrusion_2 2D Straight Skeleton Extrusion
|
|||
Stream_lines_2 2D Placement of Streamlines
|
||||
Surface_mesh_approximation Triangulated Surface Mesh Approximation
|
||||
Surface_mesh_deformation Triangulated Surface Mesh Deformation
|
||||
Surface_mesh_decomposition Triangulated Surface Mesh Decomposition
|
||||
Surface_mesher 3D Surface Mesh Generation
|
||||
Surface_mesh Surface Mesh
|
||||
Surface_mesh_parameterization Triangulated Surface Mesh Parameterization
|
||||
|
|
|
|||
|
|
@ -0,0 +1,211 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Approximate_convex_decomposition_dialog</class>
|
||||
<widget class="QDialog" name="Approximate_convex_decomposition_dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>448</width>
|
||||
<height>225</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Approximate Convex Decomposition</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="4">
|
||||
<widget class="QLabel" name="objectName">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>15</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Approximate Convex Decomposition</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="angleLabel">
|
||||
<property name="text">
|
||||
<string>Maximum number of components</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QLabel" name="approxLabel">
|
||||
<property name="text">
|
||||
<string>Number of voxels</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="sizingLabel_2">
|
||||
<property name="text">
|
||||
<string>Maximum decomposition depth</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2" colspan="2">
|
||||
<widget class="QSpinBox" name="maximumConvexHulls">
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>16</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="sizingLabel">
|
||||
<property name="text">
|
||||
<string>Volume error</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="3">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2" colspan="2">
|
||||
<widget class="QSpinBox" name="maximumDepth">
|
||||
<property name="minimum">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2" colspan="2">
|
||||
<widget class="DoubleEdit" name="volumeError">
|
||||
<property name="text">
|
||||
<string>0.01</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="2">
|
||||
<widget class="QCheckBox" name="splitConcavity">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LayoutDirection::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Split at concavity</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2" colspan="2">
|
||||
<widget class="QSpinBox" name="numVoxels">
|
||||
<property name="minimum">
|
||||
<number>512</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000000000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>100000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1000000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="2">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<widget class="QCheckBox" name="refitting">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LayoutDirection::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Refit to mesh</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>DoubleEdit</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>CGAL_double_edit.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Approximate_convex_decomposition_dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>384</x>
|
||||
<y>191</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>195</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Approximate_convex_decomposition_dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>384</x>
|
||||
<y>191</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>195</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <CGAL/Three/CGAL_Lab_plugin_interface.h>
|
||||
#include <CGAL/approximate_convex_decomposition.h>
|
||||
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||
#include "ui_Approximate_convex_decomposition_dialog.h"
|
||||
|
||||
#include <CGAL/Timer.h>
|
||||
#include <CGAL/Three/Three.h>
|
||||
#include <CGAL/Three/Scene_group_item.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QAction>
|
||||
#include <QMainWindow>
|
||||
#include <QMenu>
|
||||
#include <QApplication>
|
||||
#include <QtPlugin>
|
||||
#include <QThread>
|
||||
#include "Scene_surface_mesh_item.h"
|
||||
#include "Color_map.h"
|
||||
#include <QInputDialog>
|
||||
#include <QStringList>
|
||||
#include <QMessageBox>
|
||||
#include <QAbstractButton>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
using namespace CGAL::Three;
|
||||
|
||||
class CGAL_Lab_approximate_convex_decomposition_plugin
|
||||
: public QObject,
|
||||
protected CGAL_Lab_plugin_interface
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES(CGAL::Three::CGAL_Lab_plugin_interface)
|
||||
Q_PLUGIN_METADATA(IID "com.geometryfactory.CGALLab.PluginInterface/1.0")
|
||||
|
||||
using Convex_hull = std::pair<std::vector<EPICK::Point_3>, std::vector<std::array<unsigned int, 3> > >;
|
||||
|
||||
private:
|
||||
QAction* actionApproximateConvexDecomposition;
|
||||
|
||||
Scene_interface *scene;
|
||||
QMainWindow *mw;
|
||||
|
||||
public:
|
||||
void init(QMainWindow* mainWindow,
|
||||
Scene_interface* scene_interface,
|
||||
Messages_interface*)
|
||||
{
|
||||
this->scene = scene_interface;
|
||||
this->mw = mainWindow;
|
||||
|
||||
actionApproximateConvexDecomposition = new QAction(tr("Approximate Convex Decomposition"), mw);
|
||||
actionApproximateConvexDecomposition->setProperty("subMenuName", "Polygon Mesh Processing");
|
||||
connect(actionApproximateConvexDecomposition, SIGNAL(triggered()),
|
||||
this, SLOT(approximate_convex_decomposition()));
|
||||
}
|
||||
|
||||
bool applicable(QAction* action) const
|
||||
{
|
||||
if(action == actionApproximateConvexDecomposition)
|
||||
{
|
||||
if(scene->selectionIndices().size() == 1)
|
||||
{
|
||||
const int index = scene->mainSelectionIndex();
|
||||
return (qobject_cast<Scene_surface_mesh_item*>(scene->item(index)));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QList<QAction*> actions() const
|
||||
{
|
||||
return QList<QAction*>() << actionApproximateConvexDecomposition;
|
||||
}
|
||||
|
||||
public Q_SLOTS:
|
||||
void approximate_convex_decomposition();
|
||||
}; // class CGAL_Lab_approximate_convex_decomposition_plugin
|
||||
|
||||
void
|
||||
CGAL_Lab_approximate_convex_decomposition_plugin::
|
||||
approximate_convex_decomposition()
|
||||
{
|
||||
Scene_surface_mesh_item* sm_item = nullptr;
|
||||
|
||||
if (scene->selectionIndices().size() != 1)
|
||||
return;
|
||||
|
||||
sm_item = qobject_cast<Scene_surface_mesh_item*>(scene->item(scene->selectionIndices().front()));
|
||||
if (sm_item == nullptr)
|
||||
return;
|
||||
|
||||
QDialog dialog(mw);
|
||||
Ui::Approximate_convex_decomposition_dialog ui;
|
||||
ui.setupUi(&dialog);
|
||||
|
||||
int i = dialog.exec();
|
||||
if (i == QDialog::Rejected)
|
||||
return;
|
||||
|
||||
const unsigned int maximum_depth = static_cast<unsigned int>(ui.maximumDepth->value());
|
||||
const unsigned int maximum_convex_hulls = static_cast<unsigned int>(ui.maximumConvexHulls->value());
|
||||
const unsigned int num_voxels = static_cast<unsigned int>(ui.numVoxels->value());
|
||||
const double volume_error = ui.volumeError->value();
|
||||
const bool split_concavity = ui.splitConcavity->isChecked();
|
||||
const bool refitting = ui.refitting->isChecked();
|
||||
|
||||
QApplication::setOverrideCursor(Qt::WaitCursor);
|
||||
|
||||
std::vector<Convex_hull> convex_volumes;
|
||||
convex_volumes.reserve(maximum_convex_hulls);
|
||||
|
||||
CGAL::approximate_convex_decomposition(*(sm_item->face_graph()), std::back_inserter(convex_volumes),
|
||||
CGAL::parameters::maximum_depth(maximum_depth)
|
||||
.volume_error(volume_error)
|
||||
.maximum_number_of_convex_volumes(maximum_convex_hulls)
|
||||
.split_at_concavity(split_concavity)
|
||||
.refitting(refitting)
|
||||
.maximum_number_of_voxels(num_voxels)
|
||||
.concurrency_tag(CGAL::Parallel_if_available_tag()));
|
||||
|
||||
std::vector<QColor> distinct_colors;
|
||||
// the first color is either the background or the unique domain
|
||||
|
||||
compute_deterministic_color_map(QColor(80, 250, 80), convex_volumes.size(), std::back_inserter(distinct_colors));
|
||||
|
||||
Scene_group_item* group = new Scene_group_item(tr("%1 %2 decomposition").arg(sm_item->name()).arg(maximum_convex_hulls));
|
||||
scene->addItem(group);
|
||||
|
||||
for (std::size_t i = 0; i < convex_volumes.size(); i++) {
|
||||
Convex_hull& ch = convex_volumes[i];
|
||||
SMesh sm;
|
||||
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(ch.first, ch.second, sm);
|
||||
|
||||
Scene_surface_mesh_item* component_item = new Scene_surface_mesh_item(std::move(sm));
|
||||
component_item->setName(tr("%1 %2").arg(sm_item->name()).arg(i));
|
||||
component_item->setColor(distinct_colors[i]);
|
||||
Three::scene()->addItem(component_item);
|
||||
Three::scene()->changeGroup(component_item, group);
|
||||
}
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
QApplication::setOverrideCursor(Qt::BusyCursor);
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
|
||||
#include "Approximate_convex_decomposition_plugin.moc"
|
||||
|
|
@ -1,5 +1,17 @@
|
|||
include(CGALlab_macros)
|
||||
|
||||
cgal_lab_plugin(nef_plugin Nef_plugin)
|
||||
target_link_libraries(nef_plugin PRIVATE scene_nef_polyhedron_item scene_surface_mesh_item)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
cgal_lab_plugin(nef_plugin Nef_plugin)
|
||||
|
||||
qt6_wrap_ui(acdUI_FILES Approximate_convex_decomposition_dialog.ui)
|
||||
cgal_lab_plugin(acd_plugin Approximate_convex_decomposition_plugin ${acdUI_FILES})
|
||||
|
||||
target_link_libraries(nef_plugin PRIVATE scene_nef_polyhedron_item scene_surface_mesh_item)
|
||||
target_link_libraries(acd_plugin PRIVATE scene_surface_mesh_item)
|
||||
|
||||
find_package(TBB QUIET)
|
||||
include(CGAL_TBB_support)
|
||||
if(TARGET CGAL::TBB_support)
|
||||
target_link_libraries(acd_plugin PRIVATE CGAL::TBB_support)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ polyhedra in a sorted way and add them one by one to `Nef_nary_union_3`.
|
|||
|
||||
|
||||
*/
|
||||
template< typename Nef_polyhedron_3 >
|
||||
template< typename NefPolyhedron_3 >
|
||||
class Nef_nary_union_3 {
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ namespace CGAL {
|
|||
`Quotient<Gmpz>` or any other number type modeling \f$\mathbb{Q}\f$.
|
||||
|
||||
The second parameter and the third parameter are for future considerations.
|
||||
Neither `Nef_polyhedronItems_3` nor `Nef_polyhedronMarks` is
|
||||
Neither `NefPolyhedronItems_3` nor `Nef_polyhedronMarks` is
|
||||
specified, yet. Do not use any other than the default types for these two
|
||||
template parameters.
|
||||
|
||||
|
|
@ -49,8 +49,8 @@ namespace CGAL {
|
|||
\sa `CGAL::Polyhedron_3<Traits>`
|
||||
|
||||
*/
|
||||
template< class Nef_polyhedronTraits_3,
|
||||
class Nef_polyhedronItems_3 = CGAL::Default_items<Nef_polyhedronTraits_3>
|
||||
template< class NefPolyhedronTraits_3,
|
||||
class NefPolyhedronItems_3 = CGAL::Default_items<NefPolyhedronTraits_3>
|
||||
class Nef_polyhedronMarks = bool
|
||||
> class Nef_polyhedron_3 {
|
||||
public:
|
||||
|
|
@ -79,7 +79,7 @@ public:
|
|||
function `twin()` returns the opposite halfedge.
|
||||
|
||||
Looking at the incidence structure on a sphere map, the member function
|
||||
`out_sedge` returns the first outgoing shalfedge, and `incident_sface`
|
||||
`out_sedge()` returns the first outgoing shalfedge, and `incident_sface()`
|
||||
returns the incident sface.
|
||||
|
||||
\cgalHeading{Creation}
|
||||
|
|
@ -946,7 +946,7 @@ public:
|
|||
}; /* end Volume */
|
||||
|
||||
/*!
|
||||
traits class selected for `Nef_polyhedronTraits_3`.
|
||||
traits class selected for `NefPolyhedronTraits_3`.
|
||||
*/
|
||||
typedef unspecified_type Traits;
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ namespace CGAL {
|
|||
\ingroup PkgNef3IOFunctions
|
||||
|
||||
This function creates a 3D Nef polyhedron from an OFF file which
|
||||
is read from input stream `in`. The purpose of `OFF_to_nef_3`
|
||||
is read from input stream `in`. The purpose of `OFF_to_nef_3()`
|
||||
is to create a Nef polyhedron from an OFF file that cannot be handled
|
||||
by the `Nef_polyhedron_3` constructors. It handles double
|
||||
coordinates while using a homogeneous kernel, non-coplanar facets,
|
||||
|
|
@ -12,10 +12,12 @@ surfaces with boundaries, self-intersecting surfaces, and single
|
|||
facets. Every closed volume gets marked. The function returns the
|
||||
number of facets it could not handle.
|
||||
|
||||
@tparam NefPolyhedron_3 an object of type `Nef_polyhedron_3`.
|
||||
|
||||
\sa `CGAL::Nef_polyhedron_3<Traits>`
|
||||
|
||||
*/
|
||||
template<class Nef_polyhedron_3>
|
||||
std::size_t OFF_to_nef_3(std::istream& in, Nef_polyhedron_3& N);
|
||||
template<class NefPolyhedron_3>
|
||||
std::size_t OFF_to_nef_3(std::istream& in, NefPolyhedron_3& N);
|
||||
|
||||
} /* namespace CGAL */
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
namespace CGAL {
|
||||
|
||||
/// \ingroup PkgNef3IOFunctions
|
||||
/// Converts an object of type `Nef_polyhedron_3` into a polygon mesh model of `MutableFaceGraph`.
|
||||
/// Converts an object of type `NefPolyhedron_3` into a polygon mesh model of `MutableFaceGraph`.
|
||||
/// Note that contrary to `Nef_polyhedron_3::convert_to_polyhedron()`, the output is not triangulated
|
||||
/// (but faces with more than one connected component of the boundary).
|
||||
/// The polygon mesh can be triangulated by setting `triangulate_all_faces` to `true` or by calling the function `triangulate_faces()`.
|
||||
/// @tparam Nef_polyhedron an object of type `Nef_polyhedron_3`.
|
||||
/// @tparam Polygon_mesh a model of `MutableFaceGraph` with an internal property map for `CGAL::vertex_point_t`.
|
||||
/// The polygon mesh can be triangulated by setting `triangulate_all_faces` to `true` or by calling the function `Polygon_mesh_processing::triangulate_faces()`.
|
||||
/// @tparam NefPolyhedron_3 an object of type `Nef_polyhedron_3`.
|
||||
/// @tparam PolygonMesh a model of `MutableFaceGraph` with an internal property map for `CGAL::vertex_point_t`.
|
||||
///
|
||||
/// The points from `nef` to `pm` are converted using
|
||||
/// `CGAL::Cartesian_converter<NefKernel, TargetKernel>`.
|
||||
|
|
@ -17,16 +17,16 @@ namespace CGAL {
|
|||
/// @param pm the output.
|
||||
/// @param triangulate_all_faces indicates whether all the faces must be triangulated.
|
||||
///
|
||||
/// \pre `Polygon_mesh` must have an internal point property map with value type being `Nef_polyhedron_3::Point_3`.
|
||||
/// \pre `PolygonMesh` must have an internal point property map with value type being `NefPolyhedron_3::Point_3`.
|
||||
/// \pre `nef.simple()`
|
||||
|
||||
template <class Nef_polyhedron, class Polygon_mesh>
|
||||
void convert_nef_polyhedron_to_polygon_mesh(const Nef_polyhedron& nef, Polygon_mesh& pm, bool triangulate_all_faces = false);
|
||||
template <class NefPolyhedron_3, class PolygonMesh>
|
||||
void convert_nef_polyhedron_to_polygon_mesh(const NefPolyhedron_3& nef, PolygonMesh& pm, bool triangulate_all_faces = false);
|
||||
|
||||
/// \ingroup PkgNef3IOFunctions
|
||||
/// Converts an object of type `Nef_polyhedron_3` into a polygon soup.
|
||||
/// Converts an object of type `NefPolyhedron_3` into a polygon soup.
|
||||
/// The polygons can be triangulated by setting `triangulate_all_faces` to `true`.
|
||||
/// @tparam Nef_polyhedron an object of type `Nef_polyhedron_3`.
|
||||
/// @tparam NefPolyhedron_3 an object of type `Nef_polyhedron_3`.
|
||||
/// @tparam PointRange a model of the concept `BackInsertionSequence`
|
||||
/// whose `value_type` is the point type
|
||||
/// @tparam PolygonRange a model of the concept
|
||||
|
|
@ -43,8 +43,8 @@ namespace CGAL {
|
|||
/// @param points the output points of the soup
|
||||
/// @param polygons the output polygons of the soup.
|
||||
/// @param triangulate_all_faces indicates whether all polygons must be triangulated.
|
||||
template <class Nef_polyhedron, typename PolygonRange, typename PointRange>
|
||||
void convert_nef_polyhedron_to_polygon_soup(const Nef_polyhedron& nef,
|
||||
template <class NefPolyhedron_3, typename PolygonRange, typename PointRange>
|
||||
void convert_nef_polyhedron_to_polygon_soup(const NefPolyhedron_3& nef,
|
||||
PointRange& points,
|
||||
PolygonRange& polygons,
|
||||
bool triangulate_all_faces = false);
|
||||
|
|
|
|||
|
|
@ -12,3 +12,4 @@ BGL
|
|||
Surface_mesh
|
||||
Polygon_mesh_processing
|
||||
GraphicsView
|
||||
Basic_viewer
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ under the topological operations `boundary`, `closure`, and
|
|||
\cgalHeading{Parameters}
|
||||
|
||||
\code
|
||||
template< class Nef_polyhedronTraits_S2,
|
||||
class Nef_polyhedronItems_S2 = CGAL::SM_items,
|
||||
template< class NefPolyhedronTraits_S2,
|
||||
class NefPolyhedronItems_S2 = CGAL::SM_items,
|
||||
class Nef_polyhedronMarks = bool >
|
||||
class Nef_polyhedron_S2;
|
||||
\endcode
|
||||
|
|
@ -30,7 +30,7 @@ modeling \f$\mathbb{Z}\f$, or `Cartesian`, `Simple_cartesian` parametrized with
|
|||
type modeling \f$\mathbb{Q}\f$.
|
||||
|
||||
The second parameter and the third parameter are for future considerations.
|
||||
Neither `Nef_polyhedronItems_S2` nor `Nef_polyhedronMarks` is
|
||||
Neither `NefPolyhedronItems_S2` nor `Nef_polyhedronMarks` is
|
||||
specified, yet. Do not use other than the default types for these two
|
||||
template parameters.
|
||||
|
||||
|
|
@ -55,8 +55,8 @@ output operator is defined in the file
|
|||
Nef polyhedra are implemented on top of a halfedge data structure and
|
||||
use linear space in the number of vertices, edges and facets.
|
||||
Operations like `empty` take constant time. The operations
|
||||
`clear`, `complement`, `interior`, `closure`,
|
||||
`boundary`, `regularization`, input and output take linear
|
||||
`clear()`, `complement()`, `interior()`, `closure()`,
|
||||
`boundary()`, `regularization()`, input and output take linear
|
||||
time. All binary set operations and comparison operations take time
|
||||
\cgalBigO{n \log n} where \f$ n\f$ is the size of the output plus the size of the
|
||||
input.
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ create_single_source_cgal_program("test_isolevel_refinement.cpp")
|
|||
create_single_source_cgal_program("test_corefinement_nm_bo.cpp")
|
||||
create_single_source_cgal_program("test_corefinement_cavities.cpp")
|
||||
create_single_source_cgal_program("issue_8730.cpp")
|
||||
create_single_source_cgal_program("test_approximate_convex_decomposition.cpp")
|
||||
create_single_source_cgal_program("issue_7164.cpp")
|
||||
# create_single_source_cgal_program("test_pmp_repair_self_intersections.cpp")
|
||||
|
||||
|
|
@ -120,6 +121,7 @@ if(TARGET CGAL::TBB_support)
|
|||
target_link_libraries(self_intersection_surface_mesh_test PRIVATE CGAL::TBB_support)
|
||||
target_link_libraries(test_autorefinement PRIVATE CGAL::TBB_support)
|
||||
target_link_libraries(test_snap_rounding PRIVATE CGAL::TBB_support)
|
||||
target_link_libraries(test_approximate_convex_decomposition PRIVATE CGAL::TBB_support)
|
||||
else()
|
||||
message(STATUS "NOTICE: Intel TBB was not found. Tests will use sequential code.")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,66 @@
|
|||
#include <CGAL/Simple_cartesian.h>
|
||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
|
||||
#include <CGAL/Polygon_mesh_processing/approximate_convex_decomposition.h>
|
||||
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||
|
||||
#include <CGAL/convexity_check_3.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using EPICK = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
using EPECK = CGAL::Exact_predicates_exact_constructions_kernel;
|
||||
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
template<typename K>
|
||||
void test(const std::string &filename, const std::vector<std::size_t> n) {
|
||||
using Point = typename K::Point_3;
|
||||
|
||||
using Convex_hull = std::pair<std::vector<Point>, std::vector<std::array<unsigned int, 3> > >;
|
||||
using Mesh = CGAL::Surface_mesh<Point>;
|
||||
|
||||
Mesh mesh;
|
||||
if (!PMP::IO::read_polygon_mesh(filename, mesh)) {
|
||||
std::cerr << "Invalid input." << std::endl;
|
||||
assert(false);
|
||||
}
|
||||
|
||||
for (std::size_t i : n) {
|
||||
std::vector<Convex_hull> convex_hulls;
|
||||
PMP::approximate_convex_decomposition(mesh, std::back_inserter(convex_hulls),
|
||||
CGAL::parameters::maximum_depth(10)
|
||||
.volume_error(0.1)
|
||||
.maximum_number_of_convex_hulls(i)
|
||||
.split_at_concavity(true)
|
||||
.maximum_number_of_voxels(1000000)
|
||||
.concurrency_tag(CGAL::Parallel_if_available_tag()));
|
||||
std::cout << convex_hulls.size() << std::endl;
|
||||
assert(convex_hulls.size() == i);
|
||||
for (std::size_t j = 0; j < convex_hulls.size(); j++) {
|
||||
Mesh m;
|
||||
PMP::polygon_soup_to_polygon_mesh(convex_hulls[j].first, convex_hulls[j].second, m);
|
||||
|
||||
assert(CGAL::is_strongly_convex_3(m, typename CGAL::Convex_hull_3::internal::Default_traits_for_Chull_3<Point>::type()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/knot2.off");
|
||||
std::cout << "Testing approximate convex decomposition with EPICK Kernel of " << filename << std::endl;
|
||||
test<EPICK>(filename, {3, 6, 9});
|
||||
std::cout << "Testing approximate convex decomposition with EPECK Kernel of " << filename << std::endl;
|
||||
test<EPECK>(filename, {5});
|
||||
|
||||
std::cout << "done" << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -179,6 +179,13 @@ CGAL_add_named_parameter(tiling_t, tiling, tiling)
|
|||
CGAL_add_named_parameter(dimension_t, dimension, dimension)
|
||||
CGAL_add_named_parameter(apply_iterative_snap_rounding_t, apply_iterative_snap_rounding, apply_iterative_snap_rounding)
|
||||
CGAL_add_named_parameter(snap_grid_size_t, snap_grid_size, snap_grid_size)
|
||||
CGAL_add_named_parameter(maximum_number_of_voxels_t, maximum_number_of_voxels, maximum_number_of_voxels)
|
||||
CGAL_add_named_parameter(maximum_depth_t, maximum_depth, maximum_depth)
|
||||
CGAL_add_named_parameter(refitting_t, refitting, refitting)
|
||||
CGAL_add_named_parameter(volume_error_t, volume_error, volume_error)
|
||||
CGAL_add_named_parameter(maximum_number_of_convex_volumes_t, maximum_number_of_convex_volumes, maximum_number_of_convex_volumes)
|
||||
CGAL_add_named_parameter(split_at_concavity_t, split_at_concavity, split_at_concavity)
|
||||
|
||||
|
||||
// List of named parameters that we use in the package 'Surface Mesh Simplification'
|
||||
CGAL_add_named_parameter(get_cost_policy_t, get_cost_policy, get_cost)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1 @@
|
|||
GeometryFactory
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
AABB_tree
|
||||
Algebraic_foundations
|
||||
Arithmetic_kernel
|
||||
BGL
|
||||
CGAL_Core
|
||||
Cartesian_kernel
|
||||
Circulator
|
||||
Convex_hull_2
|
||||
Convex_hull_3
|
||||
Distance_2
|
||||
Distance_3
|
||||
Filtered_kernel
|
||||
Hash_map
|
||||
Homogeneous_kernel
|
||||
Installation
|
||||
Intersections_2
|
||||
Intersections_3
|
||||
Interval_support
|
||||
Kernel_23
|
||||
Kernel_d
|
||||
Modular_arithmetic
|
||||
Number_types
|
||||
Polygon_mesh_processing
|
||||
Profiling_tools
|
||||
Property_map
|
||||
Spatial_searching
|
||||
STL_Extension
|
||||
Stream_support
|
||||
TDS_2
|
||||
Random_numbers
|
||||
|
|
@ -0,0 +1 @@
|
|||
GPL (v3 or later)
|
||||
|
|
@ -0,0 +1 @@
|
|||
GeometryFactory
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# Created by the script cgal_create_CMakeLists
|
||||
# This is the CMake script for compiling a set of CGAL applications.
|
||||
|
||||
cmake_minimum_required(VERSION 3.12...3.31)
|
||||
project(Surface_mesh_decomposition_Tests)
|
||||
|
||||
# CGAL and its components
|
||||
find_package(CGAL REQUIRED)
|
||||
create_single_source_cgal_program("test_acd.cpp")
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
|
||||
#include <CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h>
|
||||
#include <CGAL/approximate_convex_decomposition.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using K = CGAL::Exact_predicates_inexact_constructions_kernel;
|
||||
|
||||
using Point = K::Point_3;
|
||||
|
||||
using Convex_hull = std::pair<std::vector<Point>, std::vector<std::array<unsigned int, 3> > >;
|
||||
using Mesh = CGAL::Surface_mesh<Point>;
|
||||
namespace PMP = CGAL::Polygon_mesh_processing;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/knot2.off");
|
||||
std::cout << filename << std::endl;
|
||||
|
||||
Mesh mesh;
|
||||
|
||||
std::vector<Convex_hull> convex_volumes;
|
||||
|
||||
// Try with empty mesh
|
||||
CGAL::approximate_convex_decomposition(mesh, std::back_inserter(convex_volumes),
|
||||
CGAL::parameters::maximum_depth(10)
|
||||
.volume_error(0.1)
|
||||
.maximum_number_of_convex_volumes(9)
|
||||
.split_at_concavity(true)
|
||||
.maximum_number_of_voxels(1000000)
|
||||
.concurrency_tag(CGAL::Parallel_if_available_tag()));
|
||||
|
||||
assert(convex_volumes.size() == 0);
|
||||
|
||||
if (!PMP::IO::read_polygon_mesh(filename, mesh)) {
|
||||
std::cerr << "Invalid input." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
convex_volumes.reserve(9);
|
||||
|
||||
CGAL::approximate_convex_decomposition(mesh, std::back_inserter(convex_volumes),
|
||||
CGAL::parameters::maximum_depth(10)
|
||||
.volume_error(0.1)
|
||||
.maximum_number_of_convex_hulls(9)
|
||||
.split_at_concavity(true)
|
||||
.maximum_number_of_voxels(1000000)
|
||||
.concurrency_tag(CGAL::Parallel_if_available_tag()));
|
||||
|
||||
assert(convex_volumes.size() > 0);
|
||||
assert(convex_volumes.size() <= 9);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue