deal.II version GIT relicensing-6842-g793a97d2aa 2026-10-02 14:00:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
triangulation.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2022 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13#ifndef dealii_cgal_triangulation_h
14#define dealii_cgal_triangulation_h
15
16#include <deal.II/base/config.h>
17
20
22
23#include <deal.II/grid/tria.h>
25
26#ifdef DEAL_II_WITH_CGAL
28
29# include <boost/hana.hpp>
30
31# include <CGAL/version.h>
32# if CGAL_VERSION_MAJOR >= 6
33# include <CGAL/Installation/internal/disable_deprecation_warnings_and_errors.h>
34# endif
35# include <CGAL/Complex_2_in_triangulation_3.h>
36# include <CGAL/IO/facets_in_complex_2_to_triangle_mesh.h>
37# include <CGAL/Implicit_surface_3.h>
38# include <CGAL/Labeled_mesh_domain_3.h>
39# include <CGAL/Mesh_complex_3_in_triangulation_3.h>
40# include <CGAL/Mesh_criteria_3.h>
41# include <CGAL/Mesh_triangulation_3.h>
42# include <CGAL/Polyhedron_3.h>
43# include <CGAL/Surface_mesh.h>
44# include <CGAL/Surface_mesh_default_triangulation_3.h>
45# include <CGAL/Triangulation_2.h>
46# include <CGAL/Triangulation_3.h>
47# include <CGAL/make_mesh_3.h>
48# include <CGAL/make_surface_mesh.h>
49
50#endif
51
53
54#ifdef DEAL_II_WITH_CGAL
55namespace CGALWrappers
56{
87 template <int spacedim, typename CGALTriangulation>
88 void
89 add_points_to_cgal_triangulation(const std::vector<Point<spacedim>> &points,
90 CGALTriangulation &triangulation);
91
123 template <typename CGALTriangulation, int dim, int spacedim>
124 void
125 cgal_triangulation_to_dealii_triangulation(
126 const CGALTriangulation &cgal_triangulation,
127 Triangulation<dim, spacedim> &dealii_triangulation);
128
152 template <typename CGALTriangulationType,
153 typename CornerIndexType,
154 typename CurveIndexType>
155 void
156 cgal_triangulation_to_dealii_triangulation(
157 const CGAL::Mesh_complex_3_in_triangulation_3<CGALTriangulationType,
158 CornerIndexType,
159 CurveIndexType>
160 &cgal_triangulation,
161 Triangulation<3> &dealii_triangulation);
162
184 template <typename CGAL_MeshType>
185 void
186 cgal_surface_mesh_to_dealii_triangulation(const CGAL_MeshType &cgal_mesh,
187 Triangulation<2, 3> &triangulation);
188
189
190
191# ifndef DOXYGEN
192 // Template implementation
193 template <int spacedim, typename CGALTriangulation>
194 void
195 add_points_to_cgal_triangulation(const std::vector<Point<spacedim>> &points,
196 CGALTriangulation &triangulation)
197 {
198 Assert(triangulation.is_valid(),
200 "The triangulation you pass to this function should be a valid "
201 "CGAL triangulation."));
202
203 std::vector<typename CGALTriangulation::Point> cgal_points(points.size());
204 std::transform(points.begin(),
205 points.end(),
206 cgal_points.begin(),
207 [](const auto &p) {
208 return CGALWrappers::dealii_point_to_cgal_point<
209 typename CGALTriangulation::Point>(p);
210 });
211
212 triangulation.insert(cgal_points.begin(), cgal_points.end());
213 Assert(triangulation.is_valid(),
215 "The Triangulation is no longer valid after inserting the points. "
216 "Bailing out."));
217 }
218
219
220
221 template <typename CGALTriangulationType,
222 typename CornerIndexType,
223 typename CurveIndexType>
224 void
225 cgal_triangulation_to_dealii_triangulation(
226 const CGAL::Mesh_complex_3_in_triangulation_3<CGALTriangulationType,
227 CornerIndexType,
228 CurveIndexType>
229 &cgal_triangulation,
230 Triangulation<3> &dealii_triangulation)
231 {
232 using C3T3 = CGAL::Mesh_complex_3_in_triangulation_3<CGALTriangulationType,
233 CornerIndexType,
234 CurveIndexType>;
235
236 // Extract all vertices first
237 std::vector<Point<3>> dealii_vertices;
238 std::map<typename C3T3::Vertex_handle, unsigned int>
239 cgal_to_dealii_vertex_map;
240
241 std::size_t inum = 0;
242 for (auto vit = cgal_triangulation.triangulation().finite_vertices_begin();
243 vit != cgal_triangulation.triangulation().finite_vertices_end();
244 ++vit)
245 {
246 if (vit->in_dimension() <= -1)
247 continue;
248 cgal_to_dealii_vertex_map[vit] = inum++;
249 dealii_vertices.emplace_back(
250 CGALWrappers::cgal_point_to_dealii_point<3>(vit->point()));
251 }
252
253 // Now build cell connectivity
254 std::vector<CellData<3>> cells;
255 for (auto cgal_cell = cgal_triangulation.cells_in_complex_begin();
256 cgal_cell != cgal_triangulation.cells_in_complex_end();
257 ++cgal_cell)
258 {
259 CellData<3> cell(ReferenceCells::Tetrahedron.n_vertices());
260 for (unsigned int i = 0; i < 4; ++i)
261 cell.vertices[i] = cgal_to_dealii_vertex_map[cgal_cell->vertex(i)];
262 cell.manifold_id = cgal_triangulation.subdomain_index(cgal_cell);
263 cells.push_back(cell);
264 }
265
266 // Do the same with surface patches, if possible
267 SubCellData subcell_data;
268 if constexpr (std::is_integral_v<typename C3T3::Surface_patch_index>)
269 {
270 for (auto face = cgal_triangulation.facets_in_complex_begin();
271 face != cgal_triangulation.facets_in_complex_end();
272 ++face)
273 {
274 const auto &[cgal_cell, cgal_vertex_face_index] = *face;
275 CellData<2> dealii_face(ReferenceCells::Triangle.n_vertices());
276 // A face is identified by a cell and by the index within the cell
277 // of the opposite vertex. Loop over vertices, and retain only those
278 // that belong to this face.
279 int j = 0;
280 for (int i = 0; i < 4; ++i)
281 if (i != cgal_vertex_face_index)
282 dealii_face.vertices[j++] =
283 cgal_to_dealii_vertex_map[cgal_cell->vertex(i)];
284 dealii_face.manifold_id =
285 cgal_triangulation.surface_patch_index(cgal_cell,
286 cgal_vertex_face_index);
287 subcell_data.boundary_quads.emplace_back(dealii_face);
288 }
289 }
290 // and curves
291 if constexpr (std::is_integral_v<typename C3T3::Curve_index>)
292 {
293 for (auto edge = cgal_triangulation.edges_in_complex_begin();
294 edge != cgal_triangulation.edges_in_complex_end();
295 ++edge)
296 {
297 const auto &[cgal_cell, v1, v2] = *edge;
298 CellData<1> dealii_edge(ReferenceCells::Line.n_vertices());
299 dealii_edge.vertices[0] =
300 cgal_to_dealii_vertex_map[cgal_cell->vertex(v1)];
301 dealii_edge.vertices[1] =
302 cgal_to_dealii_vertex_map[cgal_cell->vertex(v2)];
303 dealii_edge.manifold_id =
304 cgal_triangulation.curve_index(cgal_cell->vertex(v1),
305 cgal_cell->vertex(v2));
306 subcell_data.boundary_lines.emplace_back(dealii_edge);
307 }
308 }
309
310 dealii_triangulation.create_triangulation(dealii_vertices,
311 cells,
312 subcell_data);
313 }
314
315
316
317 template <typename CGALTriangulation, int dim, int spacedim>
318 void
319 cgal_triangulation_to_dealii_triangulation(
320 const CGALTriangulation &cgal_triangulation,
321 Triangulation<dim, spacedim> &dealii_triangulation)
322 {
323 AssertThrow(cgal_triangulation.dimension() == dim,
324 ExcMessage("The dimension of the input CGAL triangulation (" +
325 std::to_string(cgal_triangulation.dimension()) +
326 ") does not match the dimension of the output "
327 "deal.II triangulation (" +
328 std::to_string(dim) + ")."));
329
330 Assert(dealii_triangulation.n_cells() == 0,
331 ExcMessage("The output triangulation object needs to be empty."));
332
333 // deal.II storage data structures
334 std::vector<Point<spacedim>> vertices(
335 cgal_triangulation.number_of_vertices());
336 std::vector<CellData<dim>> cells;
337 SubCellData subcell_data;
338
339 // CGAL storage data structures
340 std::map<typename CGALTriangulation::Vertex_handle, unsigned int>
341 vertex_map;
342 {
343 unsigned int i = 0;
344 for (const auto &v : cgal_triangulation.finite_vertex_handles())
345 {
346 vertices[i] =
347 CGALWrappers::cgal_point_to_dealii_point<spacedim>(v->point());
348 vertex_map[v] = i++;
349 }
350 }
351
352 const auto has_faces = boost::hana::is_valid(
353 [](auto &&obj) -> decltype(obj.finite_face_handles()) {});
354
355 const auto has_cells = boost::hana::is_valid(
356 [](auto &&obj) -> decltype(obj.finite_cell_handles()) {});
357
358 // Different loops for Triangulation_2 and Triangulation_3 types.
359 if constexpr (decltype(has_faces(cgal_triangulation)){})
360 {
361 // This is a non-degenerate Triangulation_2
362 if (cgal_triangulation.dimension() == 2)
363 for (const auto &f : cgal_triangulation.finite_face_handles())
364 {
365 CellData<dim> cell(ReferenceCells::Triangle.n_vertices());
366 for (unsigned int i = 0;
368 ++i)
369 cell.vertices[i] = vertex_map[f->vertex(i)];
370 cells.push_back(cell);
371 }
372 else if (cgal_triangulation.dimension() == 1)
373 // This is a degenerate Triangulation_2, made of edges
374 for (const auto &e : cgal_triangulation.finite_edges())
375 {
376 // An edge is identified by a face and a vertex index in the
377 // face
378 const auto &f = e.first;
379 const auto &i = e.second;
380 CellData<dim> cell(ReferenceCells::Line.n_vertices());
381 unsigned int id = 0;
382 // Since an edge is identified by a face (a triangle) and the
383 // index of the opposite vertex to the edge, we can use this
384 // logic to infer the indices of the vertices of the edge: loop
385 // over all vertices, and keep only those that are not the
386 // opposite vertex of the edge.
387 for (unsigned int j = 0;
389 ++j)
390 if (j != i)
391 cell.vertices[id++] = vertex_map[f->vertex(j)];
392 cells.push_back(cell);
393 }
394 else
395 {
397 }
398 }
399 else if constexpr (decltype(has_cells(cgal_triangulation)){})
400 {
401 // This is a non-degenerate Triangulation_3
402 if (cgal_triangulation.dimension() == 3)
403 for (const auto &c : cgal_triangulation.finite_cell_handles())
404 {
406 for (unsigned int i = 0;
407 i < ReferenceCells::Tetrahedron.n_vertices();
408 ++i)
409 cell.vertices[i] = vertex_map[c->vertex(i)];
410 cells.push_back(cell);
411 }
412 else if (cgal_triangulation.dimension() == 2)
413 // This is a degenerate Triangulation_3, made of triangles
414 for (const auto &facet : cgal_triangulation.finite_facets())
415 {
416 // A facet is identified by a cell and the opposite vertex index
417 // in the face
418 const auto &c = facet.first;
419 const auto &i = facet.second;
420 CellData<dim> cell(ReferenceCells::Triangle.n_vertices());
421 unsigned int id = 0;
422 // Since a face is identified by a cell (a tetrahedron) and the
423 // index of the opposite vertex to the face, we can use this
424 // logic to infer the indices of the vertices of the face: loop
425 // over all vertices, and keep only those that are not the
426 // opposite vertex of the face.
427 for (unsigned int j = 0;
428 j < ReferenceCells::Tetrahedron.n_vertices();
429 ++j)
430 if (j != i)
431 cell.vertices[id++] = vertex_map[c->vertex(j)];
432 cells.push_back(cell);
433 }
434 else if (cgal_triangulation.dimension() == 1)
435 // This is a degenerate Triangulation_3, made of edges
436 for (const auto &edge : cgal_triangulation.finite_edges())
437 {
438 // An edge is identified by a cell and its two vertices
439 const auto &[c, i, j] = edge;
440 CellData<dim> cell(ReferenceCells::Line.n_vertices());
441 cell.vertices[0] = vertex_map[c->vertex(i)];
442 cell.vertices[1] = vertex_map[c->vertex(j)];
443 cells.push_back(cell);
444 }
445 else
446 {
448 }
449 }
450 dealii_triangulation.create_triangulation(vertices, cells, subcell_data);
451 }
452
453
454
455 template <typename CGAL_MeshType>
456 void
457 cgal_surface_mesh_to_dealii_triangulation(const CGAL_MeshType &cgal_mesh,
458 Triangulation<2, 3> &triangulation)
459 {
460 Assert(triangulation.n_cells() == 0,
462 "Triangulation must be empty upon calling this function."));
463
464 const auto is_surface_mesh =
465 boost::hana::is_valid([](auto &&obj) -> decltype(obj.faces()) {});
466
467 const auto is_polyhedral =
468 boost::hana::is_valid([](auto &&obj) -> decltype(obj.facets_begin()) {});
469
470 // Collect Vertices and cells
471 std::vector<::Point<3>> vertices;
472 std::vector<CellData<2>> cells;
473 SubCellData subcell_data;
474
475 // Different loops for Polyhedron or Surface_mesh types
476 if constexpr (decltype(is_surface_mesh(cgal_mesh)){})
477 {
478 AssertThrow(cgal_mesh.num_vertices() > 0,
479 ExcMessage("CGAL surface mesh is empty."));
480 vertices.reserve(cgal_mesh.num_vertices());
481 std::map<typename CGAL_MeshType::Vertex_index, unsigned int> vertex_map;
482 {
483 unsigned int i = 0;
484 for (const auto &v : cgal_mesh.vertices())
485 {
486 vertices.emplace_back(CGALWrappers::cgal_point_to_dealii_point<3>(
487 cgal_mesh.point(v)));
488 vertex_map[v] = i++;
489 }
490 }
491
492 // Collect CellData
493 for (const auto &face : cgal_mesh.faces())
494 {
495 const auto face_vertices =
496 CGAL::vertices_around_face(cgal_mesh.halfedge(face), cgal_mesh);
497
498 AssertThrow(face_vertices.size() == 3 || face_vertices.size() == 4,
499 ExcMessage("Only triangle or quadrilateral surface "
500 "meshes are supported in deal.II"));
501
502 CellData<2> c(face_vertices.size());
503 unsigned int vertex_no = 0;
504 for (const auto &v : face_vertices)
505 c.vertices[vertex_no++] = vertex_map[v];
506
507 // Make sure the numberfing is consistent with the one in deal.II
508 if (face_vertices.size() == 4)
509 std::swap(c.vertices[3], c.vertices[2]);
510
511 cells.emplace_back(c);
512 }
513 }
514 else if constexpr (decltype(is_polyhedral(cgal_mesh)){})
515 {
516 AssertThrow(cgal_mesh.size_of_vertices() > 0,
517 ExcMessage("CGAL surface mesh is empty."));
518 vertices.reserve(cgal_mesh.size_of_vertices());
519 std::map<decltype(cgal_mesh.vertices_begin()), unsigned int> vertex_map;
520 {
521 unsigned int i = 0;
522 for (auto it = cgal_mesh.vertices_begin();
523 it != cgal_mesh.vertices_end();
524 ++it)
525 {
526 vertices.emplace_back(
527 CGALWrappers::cgal_point_to_dealii_point<3>(it->point()));
528 vertex_map[it] = i++;
529 }
530 }
531
532 // Loop over faces of Polyhedron, fill CellData
533 for (auto face = cgal_mesh.facets_begin();
534 face != cgal_mesh.facets_end();
535 ++face)
536 {
537 auto j = face->facet_begin();
538 const unsigned int vertices_per_face = CGAL::circulator_size(j);
539 AssertThrow(vertices_per_face == 3 || vertices_per_face == 4,
540 ExcMessage("Only triangle or quadrilateral surface "
541 "meshes are supported in deal.II. You "
542 "tried to read a mesh where a face has " +
543 std::to_string(vertices_per_face) +
544 " vertices per face."));
545
546 CellData<2> c(vertices_per_face);
547 for (unsigned int vertex_no = 0; vertex_no < vertices_per_face;
548 ++vertex_no)
549 {
550 c.vertices[vertex_no] = vertex_map[j->vertex()];
551 ++j;
552 }
553
554 if (vertices_per_face == 4)
555 std::swap(c.vertices[3], c.vertices[2]);
556
557 cells.emplace_back(c);
558 }
559 }
560 else
561 {
562 AssertThrow(false,
564 "Unsupported CGAL surface triangulation type."));
565 }
566 triangulation.create_triangulation(vertices, cells, subcell_data);
567 }
568} // namespace CGALWrappers
569# endif // doxygen
570
571#endif
572
574#endif
Definition point.h:111
constexpr unsigned int n_vertices() const
virtual void create_triangulation(const std::vector< Point< spacedim > > &vertices, const std::vector< CellData< dim > > &cells, const SubCellData &subcelldata)
unsigned int n_cells() const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
const unsigned int v1
#define Assert(cond, exc)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
constexpr ReferenceCell< 1 > Line
constexpr ReferenceCell< 2 > Triangle
constexpr ReferenceCell< 3 > Tetrahedron
std::vector< CellData< 2 > > boundary_quads
Definition cell_data.h:247
std::vector< CellData< 1 > > boundary_lines
Definition cell_data.h:231