Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mesh_3 - add surface_only() named parameter #8781

Merged
merged 16 commits into from
Mar 28, 2025
Merged
3 changes: 2 additions & 1 deletion Installation/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
- Added two new meshing parameters that enable mesh initialization customization :
- `initial_points_generator` : enables the user to specify a functor that generates initial points,
- `initial_points` : enables the user to specify a `Range` of initial points.

- Added a new meshing parameter `surface_only`, to improve performances when the user is only interested in surface mesh generation.

### [2D Triangulations](https://doc.cgal.org/6.1/Manual/packages.html#PkgTriangulation2)

Expand All @@ -43,6 +43,7 @@
### Triangulations
- All triangulations now offer the functions `point(Vertex_handle)` and `point(Simplex, int)`, which enables users to access the geometric position of a vertex and of the i-th vertex of a simplex of a triangulation.


## [Release 6.0.1](https://github.com/CGAL/cgal/releases/tag/v6.0.1)

### [Poisson Surface Reconstruction](https://doc.cgal.org/6.0.1/Manual/packages.html#PkgPoissonSurfaceReconstruction3)
Expand Down
16 changes: 16 additions & 0 deletions Mesh_3/doc/Mesh_3/CGAL/Mesh_3/parameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,22 @@ unspecified_type no_exude();
*/
unspecified_type no_features();

/*!
* \ingroup PkgMesh3Parameters
*
* The function `parameters::surface_only()`
* enables the meshing algorithm
* to mesh the input surface only and not take the volume into account.
*
* When this option is enabled, the ouput mesh has no cells in the 3D complex,
* only facets, edges and vertices.
* Full-3D optimization steps such as mesh perturbation and mesh exudation are automatically disabled.
*
* \sa `CGAL::make_mesh_3()`
* \sa `CGAL::refine_mesh_3()`
*/
unspecified_type surface_only();

/*!
* \ingroup PkgMesh3Parameters
*
Expand Down
6 changes: 4 additions & 2 deletions Mesh_3/doc/Mesh_3/Mesh_3.txt
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,6 @@ View of 3D meshes produced from a polyhedral domain. (i) is a view of file out_1
In the following example we have a "bounding polyhedron" which defines the meshing domain and two surfaces inside this domain.
The surfaces inside the domain may be closed surfaces as well as surfaces with boundaries. In the case of a closed surface
the volume delimited by this surface is also considered as inside the domain.
previous subsection.


\cgalExample{Mesh_3/mesh_polyhedral_domain_with_surface_inside.cpp}
Expand All @@ -678,7 +677,10 @@ View of 3D meshes produced from a polyhedral domain with a nested surface.
\subsubsection Mesh_3RemeshingPolyhedralSurface Remeshing a Polyhedral Surface

The following code creates a polyhedral domain, with only one polyhedron,
and no "bounding polyhedron", so the volumetric part of the domain will be empty.
and no "bounding polyhedron". The volumetric part of the domain may be empty
and should not be meshed.
In this case, it is recommended to use the `CGAL::parameters::surface_only()` parameter
to speedup the meshing of the surface only.
This enables to remesh a surface, and is equivalent to the function `make_surface_mesh()`.

\cgalExample{Mesh_3/remesh_polyhedral_surface.cpp}
Expand Down
11 changes: 4 additions & 7 deletions Mesh_3/examples/Mesh_3/remesh_polyhedral_surface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,10 @@ int main()
return EXIT_FAILURE;
}

// Create a vector with only one element: the pointer to the polyhedron.
std::vector<Polyhedron*> poly_ptrs_vector(1, &poly);

// Create a polyhedral domain, with only one polyhedron,
// and no "bounding polyhedron", so the volumetric part of the domain will be
// empty.
Mesh_domain domain(poly_ptrs_vector.begin(), poly_ptrs_vector.end());
// and no "bounding polyhedron".
// The volumetric part will be omitted by the use of `params::surface_only()`
Mesh_domain domain(poly);

// Get sharp features
domain.detect_features(); //includes detection of borders
Expand All @@ -54,7 +51,7 @@ int main()
facet_distance(0.001));

// Mesh generation
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria, params::no_perturb().no_exude());
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria, params::surface_only());

// Output the facets of the c3t3 to an OFF file. The facets will not be
// oriented.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace CGAL
*
* Functor for generating initial points in gray images.
* This functor is a model of the `InitialPointsGenerator_3` concept,
* and can be passed as a parameter to `CGAL::make_mesh_3` using the
* and can be passed as a parameter to `CGAL::make_mesh_3()` using the
* `CGAL::parameters::initial_points_generator()` parameter function.
*
* On images that contain multiple connected components,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ struct Get_point
*
* Functor for generating initial points in labeled images.
* This functor is a model of the `InitialPointsGenerator_3` concept,
* and can be passed as a parameter to `CGAL::make_mesh_3` using the
* and can be passed as a parameter to `CGAL::make_mesh_3()` using the
* `CGAL::parameters::initial_points_generator()` parameter function.
*
* This functor scans the complete image to detect all its connected components,
Expand Down
85 changes: 49 additions & 36 deletions Mesh_3/include/CGAL/Mesh_3/Mesher_3.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ class Mesher_3
}

/// Launch mesh refinement
double refine_mesh(std::string dump_after_refine_surface_prefix = "");
double refine_mesh(std::string dump_after_refine_surface_prefix = "",
const bool surface_only = false);

/// Debug
std::string debug_info() const;
Expand Down Expand Up @@ -417,7 +418,7 @@ Mesher_3<C3T3,MC,MD>::Mesher_3(C3T3& c3t3,
template<class C3T3, class MC, class MD>
double
Mesher_3<C3T3,MC,MD>::
refine_mesh(std::string dump_after_refine_surface_prefix)
refine_mesh(std::string dump_after_refine_surface_prefix, const bool surface_only)
{
CGAL::Real_timer timer;
timer.start();
Expand Down Expand Up @@ -481,7 +482,12 @@ refine_mesh(std::string dump_after_refine_surface_prefix)

dump_c3t3(r_c3t3_, dump_after_refine_surface_prefix);

if(!forced_stop())
if(surface_only)
{
for(auto cit = r_tr.finite_cells_begin(); cit != r_tr.finite_cells_end(); ++cit)
r_c3t3_.remove_from_complex(cit);
}
else if(!forced_stop())
{
// Then scan volume and refine it
CGAL_MESH_3_TASK_BEGIN(scan_cells_task_handle);
Expand Down Expand Up @@ -575,42 +581,49 @@ refine_mesh(std::string dump_after_refine_surface_prefix)

facets_visitor_.activate();

std::cerr << "Start volume scan...";
CGAL_MESH_3_TASK_BEGIN(scan_cells_task_handle);
cells_mesher_.scan_triangulation();
CGAL_MESH_3_TASK_END(scan_cells_task_handle);
refinement_stage = REFINE_ALL;
std::cerr << "end scan. [Bad tets:" << cells_mesher_.size() << "]";
std::cerr << std::endl << std::endl;
elapsed_time += timer.time();
dump_c3t3(r_c3t3_, dump_after_refine_surface_prefix);
timer.stop(); timer.reset(); timer.start();

std::cerr << "Refining...\n";
std::cerr << "Legend of the following line: "
<< "(#vertices,#steps," << cells_mesher_.debug_info_header()
<< ")\n";
std::cerr << "(" << r_tr.number_of_vertices() << ","
<< nbsteps << "," << cells_mesher_.debug_info() << ")";

CGAL_MESH_3_TASK_BEGIN(refine_volume_mesh_task_handle);
while ( ! cells_mesher_.is_algorithm_done() &&
! forced_stop() )
if(surface_only)
{
cells_mesher_.one_step(cells_visitor_);
std::cerr
<< boost::format("\r \r"
"(%1%,%2%,%3%) (%|4$.1f| vertices/s)")
% r_tr.number_of_vertices()
% nbsteps % cells_mesher_.debug_info()
% (nbsteps / timer.time());
++nbsteps;
for(auto cit = r_tr.finite_cells_begin(); cit != r_tr.finite_cells_end(); ++cit)
r_c3t3_.remove_from_complex(cit);
}
CGAL_MESH_3_TASK_END(refine_volume_mesh_task_handle);
std::cerr << std::endl;
else
{
std::cerr << "Start volume scan...";
CGAL_MESH_3_TASK_BEGIN(scan_cells_task_handle);
cells_mesher_.scan_triangulation();
CGAL_MESH_3_TASK_END(scan_cells_task_handle);
refinement_stage = REFINE_ALL;
std::cerr << "end scan. [Bad tets:" << cells_mesher_.size() << "]";
std::cerr << std::endl << std::endl;
elapsed_time += timer.time();
dump_c3t3(r_c3t3_, dump_after_refine_surface_prefix);
timer.stop(); timer.reset(); timer.start();

std::cerr << "Refining...\n";
std::cerr << "Legend of the following line: "
<< "(#vertices,#steps," << cells_mesher_.debug_info_header()
<< ")\n";
std::cerr << "(" << r_tr.number_of_vertices() << ","
<< nbsteps << "," << cells_mesher_.debug_info() << ")";

std::cerr << "Total refining volume time: " << timer.time() << "s" << std::endl;
std::cerr << "Total refining time: " << timer.time()+elapsed_time << "s" << std::endl;
CGAL_MESH_3_TASK_BEGIN(refine_volume_mesh_task_handle);
while ( ! cells_mesher_.is_algorithm_done() &&
! forced_stop() )
{
cells_mesher_.one_step(cells_visitor_);
std::cerr
<< boost::format("\r \r"
"(%1%,%2%,%3%) (%|4$.1f| vertices/s)")
% r_tr.number_of_vertices()
% nbsteps % cells_mesher_.debug_info()
% (nbsteps / timer.time());
++nbsteps;
}
CGAL_MESH_3_TASK_END(refine_volume_mesh_task_handle);
std::cerr << std::endl;
std::cerr << "Total refining volume time: " << timer.time() << "s" << std::endl;
}
std::cerr << "Total refining time: " << timer.time() + elapsed_time << "s" << std::endl;
std::cerr << std::endl;

CGAL_postcondition(r_tr.is_valid());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ surface_Delaunay_remeshing(const TriangleMesh& tmesh,
// Mesh generation
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria,
CGAL::parameters::no_perturb(),
CGAL::parameters::no_exude());
CGAL::parameters::no_exude(),
CGAL::parameters::surface_only());

TriangleMeshOut out;
CGAL::facets_in_complex_3_to_triangle_mesh(c3t3, out);
Expand Down
19 changes: 14 additions & 5 deletions Mesh_3/include/CGAL/Polyhedral_mesh_domain_3.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ The class `Polyhedral_mesh_domain_3` implements
a domain defined by a simplicial polyhedral surface.

The input polyhedral surface must be free of intersections.

It must include (at least) one closed connected component
that defines the boundary of the domain to be meshed.
Inside this bounding component,
Expand All @@ -120,6 +121,10 @@ several other components (closed or not)
that will be represented in the final mesh.
This class is a model of the concept `MeshDomain_3`.

\note When the given surface(s) are not closed, the surface only will be meshed.
It is then recommended to use the parameter `parameters::surface_only()` to speedup
the meshing process.

\tparam Polyhedron stands for the type of the input polyhedral surface(s),
model of `FaceListGraph`.

Expand Down Expand Up @@ -238,10 +243,11 @@ class Polyhedral_mesh_domain_3
/// @{

/*!
Construction from a bounding polyhedral surface which must be closed, and free of intersections.
The inside of `bounding_polyhedron` will be meshed.
Construction from a polyhedral surface which must be free of intersections.
If `polyhedron` is closed, its inside will be meshed,
otherwise there will be no interior and only the surface will be meshed.
*/
Polyhedral_mesh_domain_3(const Polyhedron& bounding_polyhedron
Polyhedral_mesh_domain_3(const Polyhedron& polyhedron
#ifndef DOXYGEN_RUNNING
, CGAL::Random* p_rng = nullptr
#endif
Expand All @@ -250,12 +256,15 @@ class Polyhedral_mesh_domain_3
, bounding_tree_(&tree_) // the bounding tree is tree_
, p_rng_(p_rng)
{
this->add_primitives(bounding_polyhedron);
if(! is_triangle_mesh(bounding_polyhedron)) {
this->add_primitives(polyhedron);
if(!is_triangle_mesh(polyhedron)) {
std::cerr << "Your input polyhedron must be triangulated!\n";
CGAL_error_msg("Your input polyhedron must be triangulated!");
}
this->build();

if(!is_closed(polyhedron))
set_surface_only();
}

/*!
Expand Down
18 changes: 13 additions & 5 deletions Mesh_3/include/CGAL/Polyhedral_mesh_domain_with_features_3.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ It is a model of the concept `MeshDomainWithFeatures_3`. It also provides
a member function to automatically detect sharp features and boundaries from
the input polyhedral surface(s).

\note When the given surface(s) are not closed, the surface only will be meshed.
It is then recommended to use the parameter `parameters::surface_only()` to speedup
the meshing process.

\tparam IGT stands for a geometric traits class providing the types
and functors required to implement the intersection tests and intersection computations
for polyhedral boundary surfaces. This parameter has to be
Expand Down Expand Up @@ -162,22 +166,26 @@ class Polyhedral_mesh_domain_with_features_3

/*!
Constructor from a polyhedral surface.
No feature detection is done at this level. Note that a copy of `bounding_polyhedron` will be done.
The polyhedron `bounding_polyhedron` has to be closed and free of intersections.
The interior of `bounding_polyhedron` will be meshed.
No feature detection is done at this level. Note that a copy of `polyhedron` will be done.
The polyhedron `polyhedron` must be free of intersections.
If `polyhedron` is closed, its inside will be meshed,
otherwise there will be no interior and only the surface will be meshed.
*/
Polyhedral_mesh_domain_with_features_3(const Polyhedron& bounding_polyhedron
Polyhedral_mesh_domain_with_features_3(const Polyhedron& polyhedron
#ifndef DOXYGEN_RUNNING
, CGAL::Random* p_rng = nullptr
#endif
)
: Base(p_rng) , borders_detected_(false)
{
stored_polyhedra.resize(1);
stored_polyhedra[0] = bounding_polyhedron;
stored_polyhedra[0] = polyhedron;
get(face_patch_id_t<Patch_id>(), stored_polyhedra[0]);
this->add_primitives(stored_polyhedra[0]);
this->build();

if(!is_closed(polyhedron))
this->set_surface_only();
}

#ifndef CGAL_NO_DEPRECATED_CODE
Expand Down
13 changes: 12 additions & 1 deletion Mesh_3/include/CGAL/make_mesh_3.h
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,17 @@ struct C3t3_initializer < C3T3, MD, MC, true, CGAL::Tag_false>
* </UL>}
* \cgalParamDefault{`parameters::features(domain)`}
* \cgalParamSectionEnd
* \cgalParamSectionBegin{Meshing surface only}
* \cgalParamDescription{If the user wants to mesh only the surface of the domain, the following named parameter
* activates this option:
* <UL>
* <LI>`parameters::surface_only()`
* </UL>
* When this parameter is used, the output `C3T3` has no complex cells,
* only complex facets, edges and vertices.
* Mesh perturbation and mesh exudation are automatically disabled.}
* \cgalParamDefault{This option is not activated, the volume is meshed according to `domain`}
* \cgalParamSectionEnd
* \cgalParamSectionBegin{Topological options (manifoldness)}
* \cgalParamDescription{In order to drive the meshing algorithm and ensure that the output mesh follows a desired topological criterion,
* three named parameters control this option:
Expand All @@ -506,7 +517,7 @@ struct C3t3_initializer < C3T3, MD, MC, true, CGAL::Tag_false>
* Two named parameters control this behavior:
* <UL>
* <LI> `parameters::no_lloyd()`
* <LI> `parameters::lloyd_optimize_mesh_3()`
* <LI> `parameters::lloyd()`
* </UL>}
* \cgalParamDefault{`parameters::no_lloyd()`}
* \cgalParamSectionEnd
Expand Down
9 changes: 5 additions & 4 deletions Mesh_3/include/CGAL/refine_mesh_3.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ class Insert_vertex_in_c3t3
* Two named parameters control this behavior:
* <UL>
* <LI> `parameters::no_lloyd()`
* <LI> `parameters::lloyd_optimize_mesh_3()`
* <LI> `parameters::lloyd()`
* </UL>}
* \cgalParamDefault{`parameters::no_lloyd()`}
* \cgalParamSectionEnd
Expand Down Expand Up @@ -355,7 +355,8 @@ void refine_mesh_3_impl(C3T3& c3t3,
, mesh_options.pointer_to_stop_atomic_boolean
#endif
);
double refine_time = mesher.refine_mesh(mesh_options.dump_after_refine_surface_prefix);
double refine_time = mesher.refine_mesh(mesh_options.dump_after_refine_surface_prefix,
mesh_options.surface_only);
c3t3.clear_manifold_info();

dump_c3t3(c3t3, mesh_options.dump_after_refine_prefix);
Expand Down Expand Up @@ -387,7 +388,7 @@ void refine_mesh_3_impl(C3T3& c3t3,
}

// Perturbation
if ( perturb )
if ( perturb && !mesh_options.surface_only )
{
double perturb_time_limit = refine_time;

Expand All @@ -403,7 +404,7 @@ void refine_mesh_3_impl(C3T3& c3t3,
}

// Exudation
if ( exude )
if ( exude && !mesh_options.surface_only )
{
double exude_time_limit = refine_time;

Expand Down
Loading