Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Installation/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

Release date: December 2026

### [Linear Cell Complex](https://doc.cgal.org/6.3/Manual/packages.html#PkgLinearCellComplex)
- added `tetrahedron_soup_to_lcc()` to import a tetrahedron soup into a linear cell complex.

### [2D and 3D Fast Intersection and Distance Computation (AABB Tree)](https://doc.cgal.org/6.3/Manual/packages.html#PkgAABBTree)
- `CGAL::AABB_tree::build()` now accepts an optional `Concurrency_tag` template parameter (`CGAL::Sequential_tag` by default).
When `CGAL::Parallel_tag` is specified, the tree construction is performed in parallel.
Expand Down
1 change: 1 addition & 0 deletions Linear_cell_complex/doc/Linear_cell_complex/Doxyfile.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@INCLUDE = ${CGAL_DOC_PACKAGE_DEFAULTS}

PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Linear Cell Complex"
INPUT += ${CGAL_PACKAGE_INCLUDE_DIR}/CGAL/tetrahedron_soup_to_lcc.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
- `CGAL::read_plane_graph_in_lcc<LCC>` (formerly `import_from_plane_graph`)
- `CGAL::triangulation_3_to_lcc<LCC,Triangulation>` (formerly `import_from_triangulation_3`)
- `CGAL::polyhedron_3_to_lcc<LCC,Polyhedron>` (formerly `import_from_polyhedron_3`)
- `CGAL::tetrahedron_soup_to_lcc()`

\cgalCRPSubsection{Operations for Linear Cell Complex}
- `CGAL::compute_normal_of_cell_0<LCC>`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ create_single_source_cgal_program("read_plane_graph_in_lcc_2.cpp")
create_single_source_cgal_program("voronoi_2.cpp")
create_single_source_cgal_program("voronoi_3.cpp")
create_single_source_cgal_program("linear_cell_complex_3_vtk_io.cpp")
create_single_source_cgal_program("tetsoup_to_lcc.cpp")

create_single_source_cgal_program("draw_linear_cell_complex.cpp")
if(CGAL_Qt6_FOUND)
target_link_libraries(draw_linear_cell_complex PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(linear_cell_complex_3_incremental_builder PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(linear_cell_complex_3_insert PRIVATE CGAL::CGAL_Basic_viewer)
target_link_libraries(tetsoup_to_lcc PRIVATE CGAL::CGAL_Basic_viewer)
endif()
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Linear_cell_complex_for_combinatorial_map.h>
#include <CGAL/tetrahedron_soup_to_lcc.h>
#include <CGAL/draw_linear_cell_complex.h>
#include <array>

#include <CGAL/IO/MEDIT.h>

using LCC = CGAL::Linear_cell_complex_for_combinatorial_map<3, 3>;
using Point_3 = CGAL::Exact_predicates_inexact_constructions_kernel::Point_3;

int main(int argc, char** argv)
{
const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/elephant.mesh");

std::vector<Point_3> points;
std::vector<std::array<std::size_t,4>> tetras;
std::vector<int> subdomains;
std::ifstream in(filename);
if(!in)
{
std::cerr << "Cannot open " << filename << std::endl;
return 1;
}

CGAL::IO::read_MEDIT(in, points, tetras, subdomains);

//filter out subdomain 0
std::vector<std::array<std::size_t,4>> filtered_tetras;
for (std::size_t i=0; i<tetras.size(); ++i)
if (subdomains[i]!=0)
filtered_tetras.push_back(tetras[i]);

LCC lcc;
CGAL::tetrahedron_soup_to_lcc(points, filtered_tetras, lcc);

CGAL::draw(lcc);
}
125 changes: 125 additions & 0 deletions Linear_cell_complex/include/CGAL/tetrahedron_soup_to_lcc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright (c) 2026 CNRS and LIRIS' Establishments (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) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr>
// Sebastien Loriot <sebastien.loriot@cgal.org>
//

#ifndef CGAL_TETRAHEDRON_SOUP_TO_LCC_H
#define CGAL_TETRAHEDRON_SOUP_TO_LCC_H

#include <boost/container_hash/hash.hpp>
#include <vector>
#include <array>
#include <unordered_map>
#include <algorithm>

namespace CGAL
{
/**
* @ingroup PkgLinearCellComplexConstructions
*
* \brief imports a 3D tetrahedron soup into a linear cell complex.
*
* Creates a 3D linear cell complex in `lcc` from a set of points and a range
* of tetrahedra defined by indices into `points`. The topological 3-combinatorial
* map is constructed by sewing adjacent 3-volumes (tetrahedra) along matching 2-faces.
*
* @tparam LCC a model of the `LinearCellComplex` concept.
* @tparam PointRange a model of `RandomAccessContainer` with `Point_3` as value type, being compatible with the point type of LCC.
* @tparam TetraRange a model of `ConstRange` where each element is a container of 4 point indices, accessible using `operator[](int)`.
*
* @param points range of 3D points.
* @param tetras range of tetrahedra, where each tetrahedron is represented by 4 indices corresponding to entries in `points`.
* @param lcc the target linear cell complex.
*
* @return a descriptor to a dart of the created complex, or `LCC::null_descriptor` if the input is empty.
*
* @pre `LCC::dimension >= 3` and `LCC::ambient_dimension == 3`.
* @pre Indices in `tetras` must be valid 0-based indices into `points`.
*
* @sa `CGAL::triangulation_3_to_lcc()`
*/
template <class LCC, class PointRange, class TetraRange>
typename LCC::Dart_descriptor
tetrahedron_soup_to_lcc(const PointRange& points,
Comment thread
sloriot marked this conversation as resolved.
const TetraRange& tetras,
LCC& lcc)
{
static_assert( LCC::dimension>=3 && LCC::ambient_dimension==3 );

if (points.empty() || tetras.empty())
return LCC::null_descriptor;

using Dart_descriptor = typename LCC::Dart_descriptor;

std::vector<typename LCC::Vertex_attribute_descriptor> vertices;
vertices.reserve(points.size());
for(const auto& pt : points)
vertices.push_back(lcc.create_vertex_attribute(pt));

struct Array_hasher
{
std::size_t operator()(const std::array<std::size_t, 3>& a) const
{
std::size_t seed = 0;
boost::hash_combine(seed, a[0]);
Comment thread
afabri marked this conversation as resolved.
boost::hash_combine(seed, a[1]);
boost::hash_combine(seed, a[2]);

return seed;
}
};

std::unordered_map< std::array<std::size_t, 3>, Dart_descriptor, Array_hasher> face_map;

Dart_descriptor dart = LCC::null_descriptor;
for (const auto& t : tetras)
{
Dart_descriptor res = lcc.make_tetrahedron(vertices[t[0]],
vertices[t[1]],
vertices[t[2]],
vertices[t[3]]);

if (dart==LCC::null_descriptor) dart = res;

for (int i=0; i<4; ++i)
{
Dart_descriptor curr= LCC::null_descriptor;
switch (i)
{
case 0: curr = lcc.template opposite<2>(lcc.next(res)); break;
case 1: curr = lcc.template opposite<2>(lcc.previous(res)); break;
case 2: curr = lcc.template opposite<2>(res); break;
default: curr = res; break;
}

std::array<std::size_t, 3> f{t[(i+1)%4],t[(i+2)%4],t[(i+3)%4]};
std::sort(f.begin(), f.end());
auto insert_res = face_map.emplace(f, curr);
if (!insert_res.second)
{
Dart_descriptor curr2=insert_res.first->second;
while (lcc.vertex_attribute(curr2) !=
lcc.vertex_attribute(lcc.other_extremity(curr)) )
{
curr2 = lcc.next(curr2);
}
lcc.template topo_sew<3>(curr, lcc.other_orientation(curr2));
}
}
}

CGAL_assertion(dart!=LCC::null_descriptor);
return dart;
}
} // end of CGAL namespace


#endif // CGAL_TETRAHEDRON_SOUP_TO_LCC_H
2 changes: 1 addition & 1 deletion Stream_support/include/CGAL/IO/MEDIT.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ bool read_MEDIT(std::istream& is,
using FT = typename Kernel_traits<Point_3>::Kernel::FT;
using Surface_patch_index = SurfacePatchIndex_;
using Facet = std::array<int, 3>;
using Tet_with_ref = std::array<int, 4>;
using Tet_with_ref = typename std::iterator_traits<typename CellRange::const_iterator>::value_type;

if(!is)
return false;
Expand Down
Loading