- Authors
- Jane Tournois, Noura Faraj, Jean-Marc Thiery, Tamy Boubekeur
Multi-Material Isotropic Tetrahedral Remeshing
This package implements an algorithm for quality tetrahedral remeshing, introduced by Faraj et al in [1]. This practical iterative remeshing algorithm is designed to remesh multi-material tetrahedral meshes, by iteratively performing a sequence of elementary operations such as edge splits, edge collapses, edge flips, and vertex relocations following a Laplacian smoothing. The algorithm results in high-quality uniform isotropic meshes, with the desired mesh density, while preserving the input geometric curve and surface features.
Remeshing Algorithm
Specific remeshing rules have been designed to satisfy the following criteria. First, the algorithm preserves the geometric complex topology, including multi-material surface patches and polyline features. Polyline features can be defined as intersections between more than two subdomains, or listed by the user. Second, it has been made possible to remesh only a selection of cells, instead of remeshing the whole domain, while preserving or remeshing the interface surfaces between the preserved and the remeshed tetrahedra.
All the local atomic operations that are performed by the algorithm preserve the input topology of the geometric complex.
The tetrahedral remeshing algorithm improves the quality of dihedral angles, while targeting the user-defined uniform sizing field and preserving the topology of the feature complex, as highlighted by Figure 61.1.
Experimental evidence shows that a higher number of remeshing iterations leads to a mesh with a improved fidelity to the sizing criterion, and higher quality dihedral angles.
Sizing Field
Tetrahedral isotropic remeshing algorithm is led by a target sizing field, that can be either uniform, adaptive, or custom.
The sizing field establishes the local target edge length for the remeshed tetrahedra. Two sizing fields are provided: a uniform and a mesh-adaptive sizing field. With CGAL::Uniform_sizing_field
, all tetrahedra edges are targeted to have equal lengths, as depicted in Section Multi-Material Isotropic Tetrahedral Remeshing . With CGAL::Adaptive_remeshing_sizing_field
, tetrahedra edge lengths remain locally close to the local size of simplices in the input mesh.
The difference between remeshing with uniform and adaptive sizing fields is depicted in Figure 61.2.
It is also possible to use a custom sizing function, like for example the same sizing field as the one used for initial tetrahedral mesh generation using the CGAL 3D Mesh Generation package, and consecutive consistent isotropic remeshing, as shown by Figure 61.3. Note that the remeshed version is denser than the one generated by CGAL::make_mesh_3()
. This happens because here, the sizing field is used both as a lower and an upper bound for edge lengths, whereas in mesh generation it is used only as an upper bound on simplex sizes.
API
The tetrahedral remeshing algorithm is implemented as a single free function CGAL::tetrahedral_isotropic_remeshing()
that takes only two required parameters: the input triangulation, and the desired edge length or sizing field, which drives the remeshing process.
Named Parameters are used to deal with optional parameters. The page Named Parameters describes their usage.
Examples
Tetrahedral Remeshing Example
The following example shows the simplest use of the tetrahedral remeshing function. The only required parameter is a given target edge length that drives the remeshing process towards a high-quality tetrahedral mesh with improved dihedral angles, and a more uniform mesh, with edge lengths getting closer to the input parameter value.
By default, the cells with a non-zero Subdomain_index
are selected for remeshing. The cells with Subdomain_index
equal to zero are ignored by the remeshing algorithm.
File Tetrahedral_remeshing/tetrahedral_remeshing_example.cpp
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Tetrahedral_remeshing/Remeshing_triangulation_3.h>
#include <CGAL/tetrahedral_remeshing.h>
#include "tetrahedral_remeshing_generate_input.h"
int main(int argc, char* argv[])
{
const double target_edge_length = (argc > 1) ? atof(argv[1]) : 0.1;
const std::size_t nbv = (argc > 2) ? atoi(argv[2]) : 1000;
Remeshing_triangulation tr;
CGAL::Tetrahedral_remeshing::insert_random_points_in_cube(nbv, tr);
for (auto cell : tr.finite_cell_handles())
cell->set_subdomain_index(1);
return EXIT_SUCCESS;
}
The class Remeshing_triangulation_3 is a class template which provides a valid triangulation type tha...
Definition: Remeshing_triangulation_3.h:59
void tetrahedral_isotropic_remeshing(CGAL::Triangulation_3< Traits, TDS, SLDS > &tr, const SizingFunction &sizing, const NamedParameters &np=parameters::default_values())
remeshes a tetrahedral mesh.
Definition: tetrahedral_remeshing.h:186
Tetrahedral Remeshing of a Selection
Optional BGL named parameters offer more precise control on the remeshing process. In this example, a triangulation with two subdomains (defined by indices stored in cells) is given as input, but only one (defined by the Subdomain_index
2) of its subdomains is remeshed.
Only the cells with a non-zero Subdomain_index
will be remeshed. The named parameter cell_is_selected_map
can be used to change this behavior.
File Tetrahedral_remeshing/tetrahedral_remeshing_of_one_subdomain.cpp
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Tetrahedral_remeshing/Remeshing_triangulation_3.h>
#include <CGAL/tetrahedral_remeshing.h>
#include <CGAL/property_map.h>
#include "tetrahedral_remeshing_generate_input.h"
using Subdomain_index = Remeshing_triangulation::Tds::Cell::Subdomain_index;
template<typename Tr>
struct Cells_of_subdomain_pmap
{
const int m_subdomain;
public:
using key_type = typename Tr::Cell_handle;
using value_type = bool;
using reference = bool;
using category = boost::read_write_property_map_tag;
friend value_type get(const Cells_of_subdomain_pmap& map,
const key_type& c)
{
return (map.m_subdomain == c->subdomain_index());
}
friend void put(Cells_of_subdomain_pmap&,
const key_type&,
const value_type)
{
;
}
};
Subdomain_index subdomain_on_side_of_plane(const K::Point_3& p,
const K::Plane_3& plane)
{
if (plane.has_on_positive_side(p))
return 1;
else
return 2;
}
K::Point_3
centroid(
const Remeshing_triangulation::Cell_handle c)
{
c->vertex(1)->point(),
c->vertex(2)->point(),
c->vertex(3)->point());
}
int main(int argc, char* argv[])
{
const double target_edge_length = (argc > 1) ? atof(argv[1]) : 0.05;
const std::size_t nbv = (argc > 2) ? atoi(argv[2]) : 1000;
const std::size_t nbv_on_plane = (argc > 3) ? atoi(argv[3]) : 100;
Remeshing_triangulation tr;
CGAL::Tetrahedral_remeshing::insert_random_points_in_cube(nbv, tr);
const K::Plane_3 plane(0, 0, 1, 0);
CGAL::Tetrahedral_remeshing::insert_points_on_plane(plane, nbv_on_plane, tr);
for (auto cell : tr.finite_cell_handles())
{
cell->set_subdomain_index(subdomain_on_side_of_plane(cc, plane));
}
target_edge_length,
CGAL::parameters::cell_is_selected_map(
Cells_of_subdomain_pmap<Remeshing_triangulation>{2}));
std::ofstream os("out.mesh");
return EXIT_SUCCESS;
}
void write_MEDIT(std::ostream &os, const T3 &t3, const NamedParameters &np=parameters::default_values())
CGAL::Point_2< Kernel > centroid(const CGAL::Point_2< Kernel > &p, const CGAL::Point_2< Kernel > &q, const CGAL::Point_2< Kernel > &r)
Tetrahedral Remeshing With Polyline Features
Optional BGL named parameters offer more precise control on the remeshing process. In this example, a triangulation with polyline features that should be preserved - though resampled - during the remeshing process, is given as input. Preserving all surfaces exactly could also be achieved by setting the named parameter remesh_boundaries
to false
.
File Tetrahedral_remeshing/tetrahedral_remeshing_with_features.cpp
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Tetrahedral_remeshing/Remeshing_triangulation_3.h>
#include <CGAL/tetrahedral_remeshing.h>
#include <CGAL/property_map.h>
#include "tetrahedral_remeshing_generate_input.h"
using Point = Remeshing_triangulation::Point;
using Vertex_handle = Remeshing_triangulation::Vertex_handle;
using Vertex_pair = std::pair<Vertex_handle, Vertex_handle>;
using Constraints_set = std::unordered_set<Vertex_pair, boost::hash<Vertex_pair>>;
using Constraints_pmap = CGAL::Boolean_property_map<Constraints_set>;
int main(int argc, char* argv[])
{
const double target_edge_length = (argc > 1) ? atof(argv[1]) : 0.02;
const int nb_iter = (argc > 2) ? atoi(argv[2]) : 1;
const int nbv = (argc > 3) ? atoi(argv[3]) : 500;
Remeshing_triangulation t3;
Constraints_set constraints;
CGAL::Tetrahedral_remeshing::generate_input_cube(nbv, t3, constraints);
make_constraints_from_cube_edges(t3, constraints);
CGAL::parameters::edge_is_constrained_map(Constraints_pmap(constraints))
.number_of_iterations(nb_iter));
return EXIT_SUCCESS;
}
It is also possible to define the polyline features as the ones stored as complex edges in a Mesh_complex_3_in_triangulation_3
(e.g., generated by the 3D Mesh Generation package).
File Tetrahedral_remeshing/mesh_and_remesh_c3t3.cpp
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>
#include <CGAL/Polyhedral_mesh_domain_with_features_3.h>
#include <CGAL/make_mesh_3.h>
#include <CGAL/property_map.h>
#include <CGAL/tetrahedral_remeshing.h>
#include <CGAL/IO/File_medit.h>
#include <string>
#include <unordered_set>
#ifdef CGAL_CONCURRENT_MESH_3
#else
#endif
Tr, Mesh_domain::Corner_index, Mesh_domain::Curve_index>;
Tr::Triangulation_data_structure>;
using Vertex_pair = std::pair<Vertex_handle, Vertex_handle>;
using Constraints_set = std::unordered_set<Vertex_pair, boost::hash<Vertex_pair>>;
using Constraints_pmap = CGAL::Boolean_property_map<Constraints_set>;
using Corners_set = std::unordered_set<Vertex_handle, boost::hash<Vertex_handle>>;
using Corners_pmap = CGAL::Boolean_property_map<Corners_set>;
int main(int argc, char* argv[])
{
const std::string fname = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/fandisk.off");
std::ifstream input(fname);
Polyhedron polyhedron;
input >> polyhedron;
std::cerr << "Error: Input invalid " << fname << std::endl;
return EXIT_FAILURE;
}
Mesh_domain domain(polyhedron);
domain.detect_features();
Mesh_criteria criteria(edge_size = 0.025,
facet_angle = 25, facet_size = 0.05, facet_distance = 0.005,
cell_radius_edge_ratio = 3, cell_size = 0.05);
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria);
Constraints_set constraints;
Constraints_pmap constraints_pmap(constraints);
Corners_set corners;
Corners_pmap corners_pmap(corners);
edge_is_constrained_map(constraints_pmap).
vertex_is_constrained_map(corners_pmap));
const double target_edge_length = 0.1;
number_of_iterations(5)
.smooth_constrained_edges(true)
.edge_is_constrained_map(constraints_pmap));
std::ofstream out("out_remeshed.mesh");
out.close();
return EXIT_SUCCESS;
}
Triangulation_data_structure::Vertex_handle Vertex_handle
bool is_triangle_mesh(const FaceGraph &g)
CGAL::Triangulation_3< typename Tr::Geom_traits, typename Tr::Triangulation_data_structure > convert_to_triangulation_3(CGAL::Mesh_complex_3_in_triangulation_3< Tr, CornerIndex, CurveIndex > c3t3, const NamedParameters &np=parameters::default_values())
converts the triangulation contained in the input to a Triangulation_3.
Definition: tetrahedral_remeshing.h:356
Tetrahedral Remeshing After Mesh Generation
The tetrahedral remeshing algorithm is designed as a post-processing for mesh generation algorithms. The API allows to generate a tetrahedral mesh with the CGAL 3D Mesh Generation package, and further improve it with the tetrahedral remeshing algorithm. This example shows how to use tetrahedral mesh generation and remeshing in sequence, from a polyhedral domain with features.
File Tetrahedral_remeshing/mesh_and_remesh_polyhedral_domain_with_features.cpp
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polyhedral_mesh_domain_with_features_3.h>
#include <CGAL/make_mesh_3.h>
#include <CGAL/tetrahedral_remeshing.h>
#include <CGAL/property_map.h>
#include <CGAL/IO/File_medit.h>
#include <string>
#include <unordered_set>
using Surface_mesh = CGAL::Surface_mesh<K::Point_3>;
#ifdef CGAL_CONCURRENT_MESH_3
#else
#endif
Tr, Mesh_domain::Corner_index, Mesh_domain::Curve_index>;
Tr::Triangulation_data_structure>;
using Vertex_pair = std::pair<Vertex_handle, Vertex_handle>;
using Constraints_set = std::unordered_set<Vertex_pair, boost::hash<Vertex_pair>>;
using Constraints_pmap = CGAL::Boolean_property_map<Constraints_set>;
int main(int argc, char* argv[])
{
const std::string fname = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/fandisk.off");
const int nb_iter = (argc > 2) ? atoi(argv[2]) : 5;
std::ifstream input(fname);
Surface_mesh mesh;
std::string filename(fname);
input >> mesh;
if (input.fail()) {
std::cerr << "Error: Cannot read file " << fname << std::endl;
return EXIT_FAILURE;
}
std::cerr << "Input geometry is not triangulated." << std::endl;
return EXIT_FAILURE;
}
Mesh_domain domain(mesh);
domain.detect_features();
const double size = 0.072;
Mesh_criteria criteria(edge_size = size,
facet_angle = 25,
facet_size = size,
facet_distance = 0.1 * size,
cell_radius_edge_ratio = 2,
cell_size = size);
Constraints_set constraints;
Constraints_pmap constraints_pmap(constraints);
edge_is_constrained_map(constraints_pmap));
number_of_iterations(nb_iter)
.edge_is_constrained_map(constraints_pmap));
std::ofstream out("out_remeshed.mesh");
out.close();
return EXIT_SUCCESS;
}
unspecified_type no_perturb()
unspecified_type no_exude()
Tetrahedral Remeshing With Sizing Field
The API enables the user to run tetrahedral remeshing with a custom sizing field, that is used as a sizing criterion to trigger mesh refinement and decimation. The following examples show how to use this type of sizing field.
In the first example, we show how to use the same sizing field as the one used as a meshing criterion for tetrahedral mesh generation using the CGAL 3D Mesh Generation package.
File Tetrahedral_remeshing/mesh_and_remesh_with_sizing.cpp
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>
#include <CGAL/Labeled_mesh_domain_3.h>
#include <CGAL/make_mesh_3.h>
#include <CGAL/tetrahedral_remeshing.h>
#ifdef CGAL_CONCURRENT_MESH_3
#else
#endif
typedef K::FT FT;
typedef K::Point_3 Point;
typedef FT (Function)(const Point&);
Tr::Triangulation_data_structure>;
struct Spherical_sizing_field
{
typedef K::FT FT;
typedef Mesh_domain::Index
Index;
FT operator()(
const Point_3& p,
const int,
const Index&)
const
{
}
};
FT sphere_function (const Point& p)
{
}
int main()
{
Mesh_domain domain = Mesh_domain::create_implicit_mesh_domain
);
Spherical_sizing_field size;
Mesh_criteria criteria(params::facet_angle(30).facet_size(0.1).facet_distance(0.025).
cell_radius_edge_ratio(2).cell_size(size));
C3t3 c3t3 = CGAL::make_mesh_3<C3t3>(domain, criteria, params::no_exude().
no_perturb());
std::cout << "Remeshing...";
std::cout.flush();
std::cout << "\rRemeshing done." << std::endl;
return EXIT_SUCCESS;
}
const CGAL::Origin ORIGIN
Kernel::FT squared_distance(Type1< Kernel > obj1, Type2< Kernel > obj2)
In the second example, we show how to use the provided adaptive sizing field, CGAL::Adaptive_remeshing_sizing_field
, that automatically sets the target edge length to keep a mesh density consistent with the density of the input mesh.
File Tetrahedral_remeshing/mesh_and_remesh_with_adaptive_sizing.cpp
#define CGAL_TETRAHEDRAL_REMESHING_VERBOSE
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedral_mesh_domain_3.h>
#include <CGAL/Mesh_triangulation_3.h>
#include <CGAL/Mesh_complex_3_in_triangulation_3.h>
#include <CGAL/Mesh_criteria_3.h>
#include <CGAL/make_mesh_3.h>
#include <CGAL/tetrahedral_remeshing.h>
#include <CGAL/Adaptive_remeshing_sizing_field.h>
#include <CGAL/IO/File_medit.h>
#include <string>
using Point = K::Point_3;
using FT = K::FT;
using Polyhedron = CGAL::Polyhedron_3<K>;
Tr::Triangulation_data_structure>;
int main(int argc, char* argv[])
{
const std::string fname = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/elk.off");
std::ifstream input(fname);
Polyhedron polyhedron;
input >> polyhedron;
if (input.fail()) {
std::cerr << "Error: Cannot read file " << fname << std::endl;
return EXIT_FAILURE;
}
std::cerr << "Input geometry is not triangulated." << std::endl;
return EXIT_FAILURE;
}
Mesh_domain domain(polyhedron);
std::cout << "Meshing...";
std::cout.flush();
Mesh_criteria criteria(facet_angle = 25,
facet_distance = 0.2,
cell_size = 10.,
cell_radius_edge_ratio = 3.);
std::cout << "\rMeshing done." << std::endl;
std::ofstream out_mesh("out_meshing.mesh");
out_mesh.close();
std::cout << "Remeshing...";
std::cout.flush();
CGAL::create_adaptive_remeshing_sizing_field(tr),
CGAL::parameters::number_of_iterations(5));
std::cout << "\rRemeshing done." << std::endl;
std::ofstream out("out_remeshing.mesh");
out.close();
return EXIT_SUCCESS;
}
Tetrahedral Remeshing from Any Tetrahedral Mesh
The following example shows how to read a mesh from a triangulation stored in a Medit file, perform tetrahedral remeshing, and save the output triangulation. The input triangulation should follow the validity requirements of a CGAL::Triangulation_3
(valid connectivity, positive orientation of the cells, and coverage of the convex hull of the vertices).
File Tetrahedral_remeshing/tetrahedral_remeshing_from_mesh.cpp
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Tetrahedral_remeshing/Remeshing_triangulation_3.h>
#include <CGAL/tetrahedral_remeshing.h>
#include <CGAL/IO/File_medit.h>
#include <fstream>
int main(int argc, char* argv[])
{
const char* filename = (argc > 1) ? argv[1] : "data/sphere.mesh";
const double target_edge_length = (argc > 2) ? atof(argv[2]) : 0.1;
Remeshing_triangulation tr;
std::ifstream is(filename, std::ios_base::in);
std::ofstream os("after_remeshing.mesh");
return EXIT_SUCCESS;
}
bool read_MEDIT(std::istream &in, T3 &t3, const NamedParameters &np=parameters::default_values())
Implementation History
This package implements the uniform version of the "Multi-Material Adaptive Volume Remesher" algorithm for quality tetrahedral remeshing, described by Noura Faraj et al. in [1].
A first version of the code was written by Noura Faraj, Jean-Marc Thiery, and Tamy Boubekeur. Jane Tournois worked on the finalization of the code, the API, and documentation.
It was initially published in CGAL-5.1.
The ability to use a sizing field as variable target edge length was introduced by Jane Tournois in CGAL-6.0.