diff --git a/Constrained_triangulation_3/include/CGAL/IO/write_MEDIT.h b/Constrained_triangulation_3/include/CGAL/IO/write_MEDIT.h index b1dd50e8483..94f48d27003 100644 --- a/Constrained_triangulation_3/include/CGAL/IO/write_MEDIT.h +++ b/Constrained_triangulation_3/include/CGAL/IO/write_MEDIT.h @@ -32,10 +32,9 @@ namespace IO { /*! * @ingroup PkgCDT3IOFunctions - * @brief outputs a conforming constrained Delaunay triangulation to - * the MEDIT (`.mesh`) file format. - * See \cgalCite{frey:inria-00069921} for a comprehensive description of this - * file format. + * @brief outputs a conforming constrained Delaunay triangulation + * using the \ref IOStreamMedit (`.mesh`). + * * @param os the output stream * @param ccdt the conforming constrained Delaunay triangulation to be written * \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below diff --git a/Documentation/doc/resources/1.14.0/BaseDoxyfile.in b/Documentation/doc/resources/1.14.0/BaseDoxyfile.in index 2cc394c72c7..a8f6401b04d 100644 --- a/Documentation/doc/resources/1.14.0/BaseDoxyfile.in +++ b/Documentation/doc/resources/1.14.0/BaseDoxyfile.in @@ -112,6 +112,7 @@ ALIASES = "cgal=%CGAL" \ "api=API" \ "vtk=VTK" \ "itk=ITK" \ + "medit=MEDIT" \ "visualstudio=Visual Studio" \ "taucs=TAUCS" \ "lapack=LAPACK" \ diff --git a/Documentation/doc/resources/1.9.6/BaseDoxyfile.in b/Documentation/doc/resources/1.9.6/BaseDoxyfile.in index 885480da05d..f95d3820402 100644 --- a/Documentation/doc/resources/1.9.6/BaseDoxyfile.in +++ b/Documentation/doc/resources/1.9.6/BaseDoxyfile.in @@ -112,6 +112,7 @@ ALIASES = "cgal=%CGAL" \ "api=API" \ "vtk=VTK" \ "itk=ITK" \ + "medit=MEDIT" \ "visualstudio=Visual Studio" \ "taucs=TAUCS" \ "lapack=LAPACK" \ diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 90e4ca2d802..9ebef6ac55e 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -63,6 +63,7 @@ Release date: December 2026 - **Breaking change** The return type as well as type of the parameter for the file name passed to IO functions is changed from `std::string` to `std::filesystem::path`, which enables to use non latin languages like Chinese for directory and file names. +- Added overloaded versions of the functions `CGAL::IO::read_MEDIT()` and `CGAL::IO::write_MEDIT()`, for indexed tetrahedron soups. ### [Basic Viewer](https://doc.cgal.org/6.3/Manual/packages.html#PkgBasicViewer) @@ -78,7 +79,6 @@ Release date: June 2026 ### General Changes - - The new list of supported compilers is: - Visual C++ 15.9, 16.10, 17.14, 18.0 (from Visual Studio 2017, 2019, 2022, and 2026) or later - Gnu g++ 13.3.0 or later (on Linux) diff --git a/Linear_cell_complex/examples/Linear_cell_complex/tetsoup_to_lcc.cpp b/Linear_cell_complex/examples/Linear_cell_complex/tetsoup_to_lcc.cpp index 93e274cc6d6..cde3eff5bec 100644 --- a/Linear_cell_complex/examples/Linear_cell_complex/tetsoup_to_lcc.cpp +++ b/Linear_cell_complex/examples/Linear_cell_complex/tetsoup_to_lcc.cpp @@ -2,10 +2,11 @@ #include #include #include +#include + #include #include -#include using LCC = CGAL::Linear_cell_complex_for_combinatorial_map<3, 3>; using Point_3 = CGAL::Exact_predicates_inexact_constructions_kernel::Point_3; @@ -23,8 +24,7 @@ int main(int argc, char** argv) std::cerr << "Cannot open " << filename << std::endl; return 1; } - - CGAL::IO::read_MEDIT(in, points, tetras, subdomains); + CGAL::IO::read_MEDIT(in, points, CGAL::parameters::tetrahedra(std::ref(tetras)).tetrahedron_refs(std::ref(subdomains))); //filter out subdomain 0 std::vector> filtered_tetras; @@ -32,8 +32,10 @@ int main(int argc, char** argv) if (subdomains[i]!=0) filtered_tetras.push_back(tetras[i]); + std::cout << points.size() << " " << filtered_tetras.size() << std::endl; LCC lcc; CGAL::tetrahedron_soup_to_lcc(lcc, points, filtered_tetras); CGAL::draw(lcc); + return 0; } diff --git a/PMP_Mesh_repair/include/CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h b/PMP_Mesh_repair/include/CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h index 6789ac6a9ca..2a0a10867d2 100644 --- a/PMP_Mesh_repair/include/CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h +++ b/PMP_Mesh_repair/include/CGAL/Polygon_mesh_processing/IO/polygon_mesh_io.h @@ -71,7 +71,7 @@ namespace IO { * \cgalParamNEnd * * \cgalParamNBegin{repair_polygon_soup} - * \cgalParamDescription{a parameter used indicate whether `CGAL::Polygon_mesh_processing::repair_polygon_soup()` + * \cgalParamDescription{a parameter used to indicate whether `CGAL::Polygon_mesh_processing::repair_polygon_soup()` * should be called on the intermediate polygon soup.} * \cgalParamType{Boolean} * \cgalParamDefault{`true`} diff --git a/Periodic_3_mesh_3/include/CGAL/Periodic_3_mesh_3/IO/File_medit.h b/Periodic_3_mesh_3/include/CGAL/Periodic_3_mesh_3/IO/File_medit.h index a16d9872e19..773c9a25835 100644 --- a/Periodic_3_mesh_3/include/CGAL/Periodic_3_mesh_3/IO/File_medit.h +++ b/Periodic_3_mesh_3/include/CGAL/Periodic_3_mesh_3/IO/File_medit.h @@ -305,7 +305,7 @@ void output_to_medit(std::ostream& os, Cell_pmap cell_pmap(c3t3); Facet_pmap facet_pmap(c3t3, cell_pmap); Facet_pmap_twice facet_pmap_twice(c3t3, cell_pmap); - Vertex_pmap vertex_pmap(c3t3, cell_pmap, facet_pmap); + Vertex_pmap vertex_pmap(c3t3); Periodic_3_mesh_3::output_to_medit(os, c3t3, occurrence_count, distinguish_copies, vertex_pmap, facet_pmap, cell_pmap, facet_pmap_twice); diff --git a/SMDS_3/include/CGAL/IO/File_medit.h b/SMDS_3/include/CGAL/IO/File_medit.h index b7741330f13..9bfbb7ca07e 100644 --- a/SMDS_3/include/CGAL/IO/File_medit.h +++ b/SMDS_3/include/CGAL/IO/File_medit.h @@ -266,7 +266,7 @@ class Renumber_surface_patches_pmap if ( elt_it != surface_map_.end() ) return elt_it->second; else - return -1; + return 0; } int get_first_unused_label(const std::set& label_set, @@ -336,83 +336,23 @@ class Use_cell_indices_pmap // ----------------------------------- // Default_vertex_pmap // ----------------------------------- -template +template class Default_vertex_pmap { - typedef typename C3T3::Surface_patch_index Surface_patch_index; - typedef typename C3T3::Subdomain_index Subdomain_index; - typedef typename C3T3::Index Index; typedef typename C3T3::Vertex_handle Vertex_handle; - typedef typename C3T3::Cell_handle Cell_handle; - typedef typename C3T3::Facet Facet; public: - Default_vertex_pmap(const C3T3& c3t3, - const Cell_pmap& c_pmap, - const Facet_pmap& f_pmap) - : c_pmap_(c_pmap) - , f_pmap_(f_pmap) - , r_c3t3_(c3t3) - , edge_index_(0) {} + Default_vertex_pmap(const C3T3& c3t3) + : r_c3t3_(c3t3) + {} int index(const Vertex_handle& vh) const { - switch ( r_c3t3_.in_dimension(vh) ) - { - case 2: - { - // Check if each incident surface facet of vh has the same surface index - typename std::vector facets; - r_c3t3_.triangulation().finite_incident_facets( - vh, std::back_inserter(facets)); - - if ( facets.begin() == facets.end() ) - return -1; - - // Look for the first surface facet - typename std::vector::iterator it_facet = facets.begin(); - while ( ! r_c3t3_.is_in_complex(*it_facet) ) - { - if ( ++it_facet == facets.end() ) - return -1; - } - - Surface_patch_index facet_index = r_c3t3_.surface_patch_index(*it_facet); - Facet facet = *it_facet; - ++it_facet; - - for( ; it_facet != facets.end() ; ++it_facet) - { - // If another index is found, return value for edge vertice - if ( r_c3t3_.is_in_complex(*it_facet) - && !( facet_index == r_c3t3_.surface_patch_index(*it_facet) ) ) - return edge_index_; - } - - return get(f_pmap_,facet); - } - break; - - case 3: - { - // Returns value of any incident cell - typename std::vector cells; - r_c3t3_.triangulation().finite_incident_cells( - vh,std::back_inserter(cells)); - - if ( cells.begin() != cells.end() ) - return get(c_pmap_, *cells.begin()); - else - return -1; - } - break; - - default: - // must not happen - return -1; - break; - } - } + if(r_c3t3_.in_dimension(vh) == 0) + return r_c3t3_.corner_index(vh); + else + return 0; + } friend int get(const Default_vertex_pmap& vmap, const Vertex_handle& vh) { @@ -420,10 +360,7 @@ class Default_vertex_pmap } private: - const Cell_pmap& c_pmap_; - const Facet_pmap& f_pmap_; const C3T3& r_c3t3_; - const unsigned int edge_index_; }; @@ -458,7 +395,7 @@ struct Medit_pmap_generator Cell_pmap; typedef Renumber_surface_patches_pmap Facet_pmap; typedef Null_pmap Facet_pmap_twice; - typedef Default_vertex_pmap Vertex_pmap; + typedef Default_vertex_pmap Vertex_pmap; bool print_twice() { return false; } }; @@ -470,7 +407,7 @@ struct Medit_pmap_generator typedef Renumber_subdomains_pmap Cell_pmap; typedef Use_cell_indices_pmap Facet_pmap; typedef Use_cell_indices_pmap Facet_pmap_twice; - typedef Default_vertex_pmap Vertex_pmap; + typedef Default_vertex_pmap Vertex_pmap; bool print_twice() { return true; } }; @@ -482,7 +419,7 @@ struct Medit_pmap_generator typedef Use_subdomain_indices Cell_pmap; typedef Use_cell_indices_pmap Facet_pmap; typedef Use_cell_indices_pmap Facet_pmap_twice; - typedef Default_vertex_pmap Vertex_pmap; + typedef Default_vertex_pmap Vertex_pmap; bool print_twice() { return true; } }; @@ -493,7 +430,7 @@ struct Medit_pmap_generator Cell_pmap; typedef Renumber_surface_patches_pmap Facet_pmap; typedef Null_pmap Facet_pmap_twice; - typedef Null_pmap Vertex_pmap; + typedef Default_vertex_pmap Vertex_pmap; bool print_twice() { return false; } }; @@ -503,7 +440,10 @@ struct Medit_pmap_generator +template bool build_triangulation_from_file(std::istream& is, Tr& tr, const bool verbose, @@ -516,45 +456,102 @@ bool build_triangulation_from_file(std::istream& is, using Subdomain_index = typename Tr::Cell::Subdomain_index; using Surface_patch_index = typename Tr::Cell::Surface_patch_index; - using Facet = std::array; // 3 = id - using Tet_with_ref = std::array; // 4 = id + using Edge = std::array; + using Facet = std::array; // 3 = id + using Tet = std::array; // 4 = id - using Edge_with_index = CGAL::IO::internal::Edge_with_index; - using Corner_with_index = CGAL::IO::internal::Corner_with_index; + using Edge_with_index = CGAL::IO::internal::Edge_with_curve_index; + using Corner_with_index = CGAL::IO::internal::Vertex_with_corner_index; - std::vector finite_cells; + std::vector finite_cells; std::vector subdomains; std::vector points; - boost::unordered_map border_facets; - std::vector edge_indices; - std::vector corner_indices; + + std::vector facets; + std::vector edges; + std::vector corners, facet_refs, edge_refs, point_refs; + std::vector ignored_ridges; bool is_CGAL_mesh = false; - if(verbose) { + + if(verbose){ std::cout << "Reading .mesh file..." << std::endl; std::cout << "Replace domain #0 = " << replace_domain_0 << std::endl; std::cout << "Allow non-manifoldness = " << allow_non_manifold << std::endl; } - bool ok = CGAL::IO::internal::read_MEDIT(is, points, finite_cells, subdomains, - border_facets, true, - edge_indices, corner_indices, - verbose, is_CGAL_mesh); - if(!ok) { + bool ok = CGAL::IO::internal::read_MEDIT(is, points, finite_cells, + facets, true, + edges, true, + ignored_ridges, false, + corners, true, + point_refs, true, + subdomains, true, + facet_refs, true, + edge_refs, true, + verbose, + is_CGAL_mesh); + if(!ok){ return false; } + std::vector edge_indices; + edge_indices.reserve(edges.size()); + for(std::size_t i = 0; i < edges.size(); ++i){ + Edge_with_index ewi; + ewi.v0 = edges[i][0]; + ewi.v1 = edges[i][1]; + ewi.curve_index = edge_refs[i]; + edge_indices.emplace_back(ewi); + } + + std::vector corner_indices; + corner_indices.reserve(corners.size()); + for(std::size_t i = 0; i < corners.size(); ++i){ + Corner_with_index cwi; + cwi.v = corners[i]; + cwi.corner_index = point_refs[i]; + corner_indices.emplace_back(cwi); + } + + boost::unordered_map border_facets; + border_facets.reserve(facets.size()); + + bool has_negative_surface_patch_ids = false; + int max_surface_patch_id{0}; + + for (std::size_t i = 0; i < facets.size(); ++i) + { + if (facet_refs[i] < 0) + has_negative_surface_patch_ids=true; + else if(facet_refs[i] > max_surface_patch_id) + max_surface_patch_id= facet_refs[i]; + border_facets.emplace(facets[i], facet_refs[i]); + } + + if(has_negative_surface_patch_ids) + { + if(verbose) + std::cerr << "Warning: negative surface patch ids" << std::endl; + for(auto& facet_and_patch_id : border_facets) { + if(facet_and_patch_id.second < 0) + facet_and_patch_id.second = max_surface_patch_id - facet_and_patch_id.second; + } + } + if(!is_CGAL_mesh) tr.may_have_badly_oriented_cells(true); - return CGAL::SMDS_3::build_triangulation_with_subdomains_range( - tr, points, finite_cells, - subdomains, border_facets, edge_indices, corner_indices, - cx_edges_oit, - verbose, - replace_domain_0 && !is_CGAL_mesh, - allow_non_manifold, - allow_negative_orientation); + return build_triangulation_with_subdomains_range(tr, + points, finite_cells, subdomains, border_facets, + edge_indices, + corner_indices, + cx_edges_oit, + verbose, + replace_domain_0 && !is_CGAL_mesh, + allow_non_manifold, + allow_negative_orientation); +return true; } template (domain, criteria); + C3t3 c3t3 = CGAL::make_mesh_3(domain, criteria, + CGAL::parameters::no_perturb().no_exude()); const std::size_t nb_facets = c3t3.number_of_facets(); const std::size_t nb_edges = c3t3.number_of_edges(); diff --git a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h index 22796bcb131..186708fefe5 100644 --- a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h +++ b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h @@ -68,6 +68,16 @@ CGAL_add_named_parameter(output_color_t, output_color, output_color) CGAL_add_named_parameter(stream_precision_t, stream_precision, stream_precision) CGAL_add_named_parameter(read_only_one_object_t, read_only_one_object, read_only_one_object) +// List of named parameters that we use in the package 'Stream_support' +CGAL_add_named_parameter(tetrahedra_t, tetrahedra, tetrahedra) +CGAL_add_named_parameter(edges_t, edges, edges) +CGAL_add_named_parameter(ridges_t, ridges, ridges) +CGAL_add_named_parameter(corners_t, corners, corners) +CGAL_add_named_parameter(tetrahedron_refs_t, tetrahedron_refs, tetrahedron_refs) +CGAL_add_named_parameter(triangle_refs_t, triangle_refs, triangle_refs) +CGAL_add_named_parameter(edge_refs_t, edge_refs, edge_refs) +CGAL_add_named_parameter(point_refs_t, point_refs, point_refs) + // List of named parameters that we use in the package 'Mesh_3' CGAL_add_named_parameter(vertex_feature_degree_t, vertex_feature_degree, vertex_feature_degree_map) @@ -434,7 +444,7 @@ CGAL_add_named_parameter_with_compatibility(cell_min_size_param_t, cell_min_size CGAL_add_named_parameter_with_compatibility(function_param_t, function_param, function) CGAL_add_named_parameter_with_compatibility(bounding_object_param_t, bounding_object_param, bounding_object) -// List of named parameters used in the package ' 3D Surface Subdivision Methods' +// List of named parameters used in the package '3D Surface Subdivision Methods' CGAL_add_named_parameter(do_not_modify_geometry_t, do_not_modify_geometry, do_not_modify_geometry) //List of named parameters used in Straight_skeleton_2 diff --git a/Stream_support/doc/Stream_support/File_formats/Supported_file_formats.txt b/Stream_support/doc/Stream_support/File_formats/Supported_file_formats.txt index 2b4c5fddbb0..5d8a52ce83f 100644 --- a/Stream_support/doc/Stream_support/File_formats/Supported_file_formats.txt +++ b/Stream_support/doc/Stream_support/File_formats/Supported_file_formats.txt @@ -543,12 +543,13 @@ A precise specification of the format is available \section IOStreamMedit Medit File Format -The Medit format, using file extension `.mesh`, is a format used by the Medit software. +The Medit format, using file extension `.mesh`, is a format used by the Medit software \cgalCite{frey:inria-00069921}. In \cgal, it is used to represent 3D meshes. A precise specification of the format is available in this report, in the appendices (Section 7.2.1, Page 36). + The following \cgal data structures can be exported into the `.mesh` file format: - `CGAL::Mesh_complex_3_in_triangulation_3`, using the function @@ -556,8 +557,9 @@ The following \cgal data structures can be exported into the `.mesh` file format - `CGAL::Conforming_constrained_Delaunay_triangulation_3`, using the function \link CGAL::IO::write_MEDIT(std::ostream &os, const CGAL::Conforming_constrained_Delaunay_triangulation_3< Traits, Tr > &ccdt, const NamedParameters& np) `CGAL::IO::write_MEDIT()` \endlink. -The following function enables to read into a tetrahedron soup -- \link CGAL::IO::read_MEDIT(std::istream&, PointRange&, CellRange&, std::vector&,bool) `CGAL::IO::read_MEDIT()` \endlink +The following function enables to read and write a tetrahedron soup +- \link CGAL::IO::read_MEDIT(std::istream&, PointRange&, const NamedParameters&) `CGAL::IO::read_MEDIT()` \endlink +- \link CGAL::IO::write_MEDIT(std::ostream&, const PointRange&, const NamedParameters&) `CGAL::IO::writeMEDIT()` \endlink \section IOStreamTetgen Tetgen File Format diff --git a/Stream_support/doc/Stream_support/IOstream.txt b/Stream_support/doc/Stream_support/IOstream.txt index 373cacf7cd5..44d270c89fc 100644 --- a/Stream_support/doc/Stream_support/IOstream.txt +++ b/Stream_support/doc/Stream_support/IOstream.txt @@ -505,8 +505,8 @@ which can perform some combinatorial repairing to ensure the input data is a 2-m \ref IOStreamMedit "MEDIT" - \link CGAL::IO::read_MEDIT(std::istream&, PointRange&, CellRange&, std::vector&,bool) `CGAL::IO::read_MEDIT()` \endlink -
-
+ \link CGAL::IO::read_MEDIT(std::istream&, PointRange&, const NamedParameters&) `CGAL::IO::read_MEDIT()` \endlink + \link CGAL::IO::write_MEDIT(std::ostream&, const PointRange&, const NamedParameters&) `CGAL::IO::write_MEDIT()` \endlink diff --git a/Stream_support/doc/Stream_support/dependencies b/Stream_support/doc/Stream_support/dependencies index 48f34988e10..3c9d7167725 100644 --- a/Stream_support/doc/Stream_support/dependencies +++ b/Stream_support/doc/Stream_support/dependencies @@ -1,6 +1,7 @@ Alpha_shapes_3 Arrangement_on_surface_2 BGL +Circulator Constrained_triangulation_3 Kernel_23 Linear_cell_complex diff --git a/Stream_support/include/CGAL/IO/3MF.h b/Stream_support/include/CGAL/IO/3MF.h index 5af92e3ac91..a781b1a8271 100644 --- a/Stream_support/include/CGAL/IO/3MF.h +++ b/Stream_support/include/CGAL/IO/3MF.h @@ -385,7 +385,7 @@ bool read_3MF(const std::filesystem::path& fname, * * \brief reads ranges of points and triangles from an input file, using the \ref IOStream3MF. * - * \attention The ranges not cleared, and the data from the file are appended. + * \note The ranges not cleared, and the data from the file are appended. * * \tparam PointRanges a model of the concepts `RandomAccessContainer` and * `BackInsertionSequence` whose `value_type` is diff --git a/Stream_support/include/CGAL/IO/GOCAD.h b/Stream_support/include/CGAL/IO/GOCAD.h index 10dd21a8c2e..0970782d7b3 100644 --- a/Stream_support/include/CGAL/IO/GOCAD.h +++ b/Stream_support/include/CGAL/IO/GOCAD.h @@ -166,7 +166,7 @@ bool read_GOCAD(std::istream& is, * * \brief reads the content of `is` into `points` and `polygons`, using the \ref IOStreamGocad. * - * \attention The polygon soup is not cleared, and the data from the stream are appended. + * \note The polygon soup is not cleared, and the data from the stream are appended. * * \tparam PointRange a model of the concept `BackInsertionSequence` whose value type is the point type * \tparam PolygonRange a model of the concept `BackInsertionSequence` whose `value_type` is @@ -214,7 +214,7 @@ bool read_GOCAD(std::istream& is, * * \brief reads the content of the file `fname` into `points` and `polygons`, using the \ref IOStreamGocad. * - * \attention The polygon soup is not cleared, and the data from the file are appended. + * \note The polygon soup is not cleared, and the data from the file are appended. * * \tparam PointRange a model of the concept `BackInsertionSequence` whose value type is the point type * \tparam PolygonRange a model of the concept `BackInsertionSequence` whose `value_type` is diff --git a/Stream_support/include/CGAL/IO/LAS.h b/Stream_support/include/CGAL/IO/LAS.h index 9a1ad644d01..857dbf82222 100644 --- a/Stream_support/include/CGAL/IO/LAS.h +++ b/Stream_support/include/CGAL/IO/LAS.h @@ -95,7 +95,7 @@ make_las_point_reader(PointMap point_map); - `LAS_property::B` with type `unsigned short` - `LAS_property::I` with type `unsigned short` - \attention To read a binary file, the flag `std::ios::binary` must be set during the creation of the `ifstream`. + \note To read a binary file, the flag `std::ios::binary` must be set during the creation of the `ifstream`. \tparam OutputIteratorValueType type of objects that can be put in `PointOutputIterator`. It must be a model of `DefaultConstructible` and defaults to `value_type_traits::%type`. @@ -125,7 +125,7 @@ bool read_LAS_with_properties(std::istream& is, Potential additional properties are ignored. - \attention To read a binary file, the flag `std::ios::binary` must be set during the creation of the `ifstream`. + \note To read a binary file, the flag `std::ios::binary` must be set during the creation of the `ifstream`. \tparam OutputIteratorValueType type of objects that can be put in `PointOutputIterator`. It must be a model of `DefaultConstructible` and defaults to `value_type_traits::%type`. @@ -237,7 +237,7 @@ make_las_point_writer(PointMap point_map); See documentation of `read_LAS_with_properties()` for the list of available `LAS_property::Tag` classes. - \attention To write to a binary file, the flag `std::ios::binary` must be set during the creation of the `ofstream`. + \note To write to a binary file, the flag `std::ios::binary` must be set during the creation of the `ofstream`. \tparam PointRange is a model of `ConstRange`. The value type of its iterator is the key type of the named parameter `point_map`. @@ -266,7 +266,7 @@ bool write_LAS_with_properties(std::ostream& os, ///< output stream. \brief writes the range of `points` (positions only), using the \ref IOStreamLAS. - \attention To write to a binary file, the flag `std::ios::binary` must be set during the creation of the `ofstream`. + \note To write to a binary file, the flag `std::ios::binary` must be set during the creation of the `ofstream`. \tparam PointRange is a model of `ConstRange`. The value type of its iterator is the key type of the named parameter `point_map`. diff --git a/Stream_support/include/CGAL/IO/MEDIT.h b/Stream_support/include/CGAL/IO/MEDIT.h index a579bff335c..f830b56313c 100644 --- a/Stream_support/include/CGAL/IO/MEDIT.h +++ b/Stream_support/include/CGAL/IO/MEDIT.h @@ -13,8 +13,10 @@ #define CGAL_IO_MEDIT_H #include +#include #include - +#include +#include #include #include #include @@ -22,6 +24,7 @@ #include #include #include +#include #include namespace CGAL { @@ -33,49 +36,178 @@ namespace CGAL { namespace IO { namespace internal { +template +struct Facet_with_patch_index +{ + int v0, v1, v2; + SurfacePatchIndex surface_patch_index; +}; + +template +struct Get_surface_path_index_type +{ + using type = int; +}; + +template +struct Get_surface_path_index_type> +{ + using type = SurfacePatchIndex; +}; + +template +struct Get_surface_path_index_type> +{ + using type = I; +}; + +template +struct Get_surface_path_index_type> +{ + using type = I; +}; + +template +using Surface_path_index_t = + typename Get_surface_path_index_type::value_type>::type; + template -struct Edge_with_index +struct Edge_with_curve_index { int v0, v1; CurveIndex curve_index; }; +template +struct Get_curve_index_type +{ + using type = int; +}; + +template +struct Get_curve_index_type> +{ + using type = CurveIndex; +}; + +template +struct Get_curve_index_type> +{ + using type = I; +}; + +template +struct Get_curve_index_type> +{ + using type = I; +}; + +template +using Curve_index_t = + typename Get_curve_index_type::value_type>::type; + template -struct Corner_with_index +struct Vertex_with_corner_index { int v; CornerIndex corner_index; + + bool operator==(const Vertex_with_corner_index& rhs) const + { + return (v == rhs.v) && (corner_index == rhs.corner_index); + } }; +template +int get_zero(const T& t) +{ + return std::get<0>(t); +} + +template +int get_one(const T& t) +{ + return std::get<1>(t); +} + +template +int get_two(const T& t) +{ + return std::get<2>(t); +} + +template +int get_zero(const Vertex_with_corner_index& t) +{ + return t.v; +} + +template +CornerIndex get_one(const Vertex_with_corner_index& t) +{ + return t.corner_index; +} + +template +int get_zero(const Edge_with_curve_index& t) +{ + return t.v0; +} + +template +int get_one(const Edge_with_curve_index& t) +{ + return t.v1; +} + +template +CurveIndex get_two(const Edge_with_curve_index& t) +{ + return t.curve_index; +} + template + class FacetRange, // either Facet or a tuple/array + class EdgeRange, // either Edge or a tuple/array + class RidgeRange, + class CornerRange, + class PointRefRange, + class CellRefRange, + class FacetRefRange, + class EdgeRefRange> // either Vertex_with_corner_index or a tuple/pair/array bool read_MEDIT(std::istream& is, PointRange& points, - CellRange& finite_cells, - std::vector& subdomains, - boost::unordered_map,SurfacePatchIndex_ >& border_facets, - bool read_border_facets, - std::vector>& edge_indices, - std::vector>& corner_indices, + CellRange& cells, + FacetRange& facets, + bool read_facets, + EdgeRange& edges, + bool read_edges, + RidgeRange& ridges, + bool read_ridges, + CornerRange& corners, + bool read_corners, + PointRefRange& point_refs, + bool read_point_refs, + CellRefRange& cell_refs, + bool read_cell_refs, + FacetRefRange& facet_refs, + bool read_facet_refs, + EdgeRefRange& edge_refs, + bool read_edge_refs, bool verbose, bool& is_CGAL_mesh) { using Point_3 = typename PointRange::value_type; - using FT = typename Kernel_traits::Kernel::FT; - using Surface_patch_index = SurfacePatchIndex_; - using Facet = std::array; - using Tet_with_ref = typename std::iterator_traits::value_type; - using Curve_index = CurveIndex_; - using Corner_index = CornerIndex_; + using FT = typename Kernel_traits::Kernel::FT; + using Facet = std::array; + using Cell_with_ref = typename std::iterator_traits::value_type; if(!is) return false; int dim; - int nv, nf, ntet, ref, ncorners, nedges; + int nv, nf, ntet, ref, nvertices, nedges, nridges; int offset = static_cast(points.size()); std::string word; @@ -130,16 +262,17 @@ bool read_MEDIT(std::istream& is, std::cerr << "Issue while reading vertices" << std::endl; return false; } - points.emplace_back(x,y,z); + points.push_back(Point_3(x,y,z)); + if(read_point_refs) + point_refs.emplace_back(ref); } } if(line.find("Triangles") != std::string::npos) { - if(read_border_facets){ - bool has_negative_surface_patch_ids = false; - Surface_patch_index max_surface_patch_id{0}; + if(read_facets){ is >> nf; + CGAL::internal::reserve(facets, nf); if(verbose) std::cerr << "Reading "<< nf << " triangles" << std::endl; @@ -147,15 +280,13 @@ bool read_MEDIT(std::istream& is, for(int i=0; i surface_patch_id; if(!(is >> n[0] >> n[1] >> n[2] >> surface_patch_id)) { if(verbose) std::cerr << "Issue while reading triangles" << std::endl; return false; } - has_negative_surface_patch_ids |= (surface_patch_id < 0); - max_surface_patch_id = (std::max)(max_surface_patch_id, surface_patch_id); Facet facet; facet[0] = offset + n[0] - 1; facet[1] = offset + n[1] - 1; @@ -173,36 +304,19 @@ bool read_MEDIT(std::istream& is, // find the circular permutation that puts the smallest index in the first place. int n0 = (std::min)({facet[0],facet[1], facet[2]}); - do + while(facet[0] != n0) { std::rotate(std::begin(facet), std::next(std::begin(facet)), std::end(facet)); } - while(facet[0] != n0); - - border_facets.emplace(facet, surface_patch_id); - } - if(has_negative_surface_patch_ids) - { - if(verbose) - std::cerr << "Warning: negative surface patch ids" << std::endl; - for(auto& facet_and_patch_id : border_facets) { - if(facet_and_patch_id.second < 0) - facet_and_patch_id.second = max_surface_patch_id - facet_and_patch_id.second; - } + facets.emplace_back(facet); // AF: why does that not compile?? {facet[0], facet[1], facet[2]}); + if(read_facet_refs) + facet_refs.emplace_back(surface_patch_id); } }else{ is >> nf; + std::string buffer; for(int i=0; i> n[0] >> n[1] >> n[2] >> surface_patch_id)) - { - if(verbose) - std::cerr << "Issue while reading triangles" << std::endl; - return false; - } - } + std::getline(is, buffer); } } if(line.find("Tetrahedra") != std::string::npos) @@ -210,17 +324,17 @@ bool read_MEDIT(std::istream& is, is >> ntet; if(verbose) - std::cerr << "Reading "<< ntet << " tetrahedra" << std::endl; + std::cerr << "Reading "<< ntet << " cells" << std::endl; for(int i=0; i> n[0] >> n[1] >> n[2] >> n[3] >> reference)) + if(!(is >> n[0] >> n[1] >> n[2] >> n[3] >> ref)) { if(verbose) - std::cerr << "Issue while reading tetrahedra" << std::endl; + std::cerr << "Issue while reading cells" << std::endl; return false; } @@ -234,74 +348,93 @@ bool read_MEDIT(std::istream& is, CGAL_warning(n[j] != n[k]); ) - Tet_with_ref t; + Cell_with_ref t; t[0] = offset + n[0] - 1; t[1] = offset + n[1] - 1; t[2] = offset + n[2] - 1; t[3] = offset + n[3] - 1; - finite_cells.push_back(t); - subdomains.push_back(reference); + cells.push_back(t); + if(read_cell_refs) + cell_refs.push_back(ref); } } - Corner_index corner_index = 0; - if(line.find("Corners") != std::string::npos) + if(line.find("Edges") != std::string::npos) { - is >> ncorners; - if(verbose && ncorners == 0) - std::cerr << "Warning: Corners section is ignored" << std::endl; + if(read_edges){ + is >> nedges; + CGAL::internal::reserve(edges, nedges); - for(int i = 0; i < ncorners; ++i) - { - int n; - if(!(is >> n)) - { - if(verbose) - std::cerr << "Issue while reading corners" << std::endl; - return false; - } - corner_indices.push_back({offset + n - 1, ++corner_index}); - } - } + if(verbose) + std::cerr << "Reading "<< nedges << " edges" << std::endl; - if(line.find("Edges") != std::string::npos) - { - is >> nedges; if(verbose && nedges == 0) - std::cerr << "Warning: Edges section is ignored" << std::endl; + std::cerr << "Warning: Edges section is empty" << std::endl; for(int i = 0; i < nedges; ++i) { int n[2]; - Curve_index curve_index = 0; - if(!(is >> n[0] >> n[1] >> curve_index)) + Curve_index_t ref = 0; + if(!(is >> n[0] >> n[1] >> ref)) { if(verbose) std::cerr << "Issue while reading edges" << std::endl; return false; } - edge_indices.push_back({offset + n[0] - 1, offset + n[1] - 1, curve_index}); - CGAL_assertion(edge_indices.size() == static_cast(i + 1)); + edges.push_back({offset + n[0] - 1, offset + n[1] - 1}); + if(read_edge_refs) + edge_refs.push_back(ref); + CGAL_assertion(edges.size() == static_cast(i + 1)); } } + else + { + is >> nedges; + std::string buffer; + for(int i=0; i> nridges; + int ci; + for(int i=0; i> ci; + if(read_ridges){ + ridges.push_back(offset + i); + } + } + } + if(line.find("Corners") != std::string::npos) + { + is >> nvertices; + int ci; + for(int i=0; i> ci; + if(read_corners){ + corners.push_back(offset + i); + } + } + } } + if (verbose) { std::cout << points.size() - std::size_t(offset) << " points" << std::endl; - std::cout << finite_cells.size() << " cells" << std::endl; - std::cout << border_facets.size() << " border facets" << std::endl; - std::cout << edge_indices.size() << " edges" << std::endl; - std::cout << corner_indices.size() << " corners" << std::endl; + std::cout << cells.size() << " cells" << std::endl; + std::cout << facets.size() << " facets" << std::endl; + std::cout << edges.size() << " edges" << std::endl; + std::cout << ridges.size() << " ridges" << std::endl; + std::cout << corners.size() << " corners" << std::endl; } - if(finite_cells.empty()) + if(cells.empty()) return false; - CGAL_assertion(finite_cells.size() == subdomains.size()); - return true; } @@ -312,47 +445,387 @@ bool read_MEDIT(std::istream& is, /*! * \ingroup PkgStreamSupportIoFuncsMEDIT * - * \brief reads the content of `is` into `points` and `finite_cells`. + * \brief reads the content of `is` into `points` and the named parameters using the \ref IOStreamMedit. * - * See \cgalCite{frey:inria-00069921} for a comprehensive description of the medit (`.mesh`) file format. * - * \attention The cell soup is not cleared, and the data from the stream are appended. + * \note Currently, only tetrahedral cells are supported. + * \note The cell soup is not cleared, and the data from the stream are appended. * - * \tparam PointRange a model of the concept `BackInsertionSequence` whose value type is the point type - * \tparam CellRange a model of the concept `BackInsertionSequence` whose `value_type` is `std::array` + * \tparam PointRange a model of `BackInsertionSequence` whose value type is the point type * * \param is the input stream * \param points points of the soup of cells - * \param finite_cells each element in it describes a cell - * using the indices of the points in `points` - * \param subdomains each element in it describes the subdomain index of the corresponding cell in `finite_cells` - * \param verbose if `true`, prints information about the reading process + * + * \param np optional \ref bgl_namedparameters "Named Parameters" described below + * + * \cgalNamedParamsBegin + * + * \cgalParamNBegin{point_refs} + * \cgalParamDescription{a non-const reference wrapper of a container of integers that will be filled by this function. + * Each element in the container corresponds to an element in `points`.} + * \cgalParamType{a `std::reference_wrapper` to a model of `BackInsertionSequence` with a value type constructible from an integer.} + * \cgalParamDefault{point references are ignored.} + * \cgalParamNEnd + * + * \cgalParamNBegin{tetrahedra} + * \cgalParamDescription{a non-const reference wrapper of a container of quadruples of integers that will be filled by this function. + * Each element represents a tetrahedron with vertices corresponding to the four integers.} + * \cgalParamType{a `std::reference_wrapper` to a model of `BackInsertionSequence` with a value type constructible using a braced initializer list of four integers.} + * \cgalParamDefault{tetrahedra are ignored.} + * \cgalParamNEnd + * + * \cgalParamNBegin{tetrahedron_refs} + * \cgalParamDescription{a non-const reference wrapper of a container of integers that will be filled by this function. + * Each element in the container corresponds to an element in `tetrahedra`.} + * \cgalParamType{a `std::reference_wrapper` to a model of `BackInsertionSequence` with a value type constructible from an integer.} + * \cgalParamDefault{tetrahedron references are ignored.} + * \cgalParamNEnd + * + * \cgalParamNBegin{triangles} + * \cgalParamDescription{a non-const reference wrapper of a container of triples of integers that will be filled by this function. + * Each element represents a triangle with vertices corresponding to the three integers.} + * \cgalParamType{a `std::reference_wrapper` to a model of `BackInsertionSequence` with a value type constructible using a braced initializer list of three integers.} + * \cgalParamDefault{triangles are ignored.} + * \cgalParamNEnd + * + * \cgalParamNBegin{triangle_refs} + * \cgalParamDescription{a non-const reference wrapper of a container of integers that will be filled by this function. + * Each element in the container corresponds to an element in `triangles`.} + * \cgalParamType{a `std::reference_wrapper` to a model of `BackInsertionSequence` with a value type constructible from an integer.} + * \cgalParamDefault{triangle references are ignored} + * \cgalParamNEnd + * + * \cgalParamNBegin{edges} + * \cgalParamDescription{a non-const reference wrapper of a container of pairs of integers that will be filled by this function. + * Each element represents an edge with vertices corresponding to the two integers.} + * \cgalParamType{a `std::reference_wrapper` to a model of `BackInsertionSequence` with a value type constructible using a braced initializer list of two integers.} + * \cgalParamDefault{edges are ignored.} + * \cgalParamNEnd + * + * \cgalParamNBegin{edge_refs} + * \cgalParamDescription{a non-const reference wrapper of a container of integers that will be filled by this function. + * Each element in the container corresponds to an element in `edges`.} + * \cgalParamType{a `std::reference_wrapper` to a model of `BackInsertionSequence` with a value type constructible from an integer.} + * \cgalParamDefault{edge references are ignored} + * \cgalParamNEnd + * + * \cgalParamNBegin{ridges} + * \cgalParamDescription{a non-const reference wrapper of a container of integers that will be filled by this function. + * Each element in the container is an index in the sequence of edges.} + * \cgalParamType{a `std::reference_wrapper` to a model of `BackInsertionSequence` with a value type constructible from an integer.} + * \cgalParamDefault{ridges are ignored.} + * \cgalParamNEnd + * + * \cgalParamNBegin{corners} + * \cgalParamDescription{a non-const reference wrapper of a container of integers that will be filled by this function. + * Each element in the container is an index in the sequence of points.} + * \cgalParamType{a `std::reference_wrapper` to a model of `BackInsertionSequence` with a value type constructible from an integer.} + * \cgalParamDefault{corners are ignored.} + * \cgalParamNEnd + * + * \cgalParamNBegin{verbose} + * \cgalParamDescription{if `true`, prints information about the reading process.} + * \cgalParamType{Boolean} + * \cgalParamDefault{`false`} + * \cgalParamNEnd + * + * \cgalNamedParamsEnd * * \returns `true` if the reading was successful, `false` otherwise. * * \see \ref IOStreamMedit */ -template +template bool read_MEDIT(std::istream& is, PointRange& points, - CellRange& finite_cells, - std::vector& subdomains, - bool verbose = false) - + const CGAL_NP_CLASS& np = parameters::default_values() +#ifndef DOXYGEN_RUNNING + , std::enable_if_t::value>* = nullptr +#endif + ) { - boost::unordered_map,int > border_facets; - constexpr bool read_border_facets = false; - std::vector> edge_indices; - std::vector> corner_indices; + using parameters::choose_parameter; + using parameters::get_parameter; + using parameters::get_parameter_reference; + + const bool verbose = choose_parameter(get_parameter(np, internal_np::verbose), false); + + // cells + std::vector> default_tetrahedra; + using Tetrahedra = typename internal_np::Lookup_named_param_def>>::reference; + static_assert(std::is_reference_v || !"tetrahedra named parameter must be a reference_wrapper"); + Tetrahedra tetrahedra = choose_parameter(get_parameter_reference(np, internal_np::tetrahedra), default_tetrahedra); + + // triangles + std::vector> default_triangles; + using Triangles = typename internal_np::Lookup_named_param_def>>::reference; + static_assert(std::is_reference_v || !"triangles named parameter must be a reference_wrapper"); + Triangles triangles = choose_parameter(get_parameter_reference(np, internal_np::triangles), default_triangles); + + // edges + std::vector> default_edges; + using Edges = typename internal_np::Lookup_named_param_def>>::reference; + static_assert(std::is_reference_v || !"edges named parameter must be a reference_wrapper"); + Edges edges = choose_parameter(get_parameter_reference(np, internal_np::edges), default_edges); + + // ridges + std::vector default_ridges; + using Ridges = typename internal_np::Lookup_named_param_def>::reference; + static_assert(std::is_reference_v || !"ridges named parameter must be a reference_wrapper"); + Ridges ridges = choose_parameter(get_parameter_reference(np, internal_np::ridges), default_ridges); + + // corners + std::vector default_corners; + using Corners = typename internal_np::Lookup_named_param_def>::reference; + static_assert(std::is_reference_v || !"corners named parameter must be a reference_wrapper"); + Corners corners = choose_parameter(get_parameter_reference(np, internal_np::corners), default_corners); + + // tetrahedron_refs + std::vector default_tetrahedron_refs; + using Tetrahedron_refs = typename internal_np::Lookup_named_param_def>::reference; + static_assert(std::is_reference_v || !"tetrahedron_refs named parameter must be a reference_wrapper"); + Tetrahedron_refs tetrahedron_refs = choose_parameter(get_parameter_reference(np, internal_np::tetrahedron_refs), default_tetrahedron_refs); + + std::vector default_triangle_refs; + using Triangle_refs = typename internal_np::Lookup_named_param_def>::reference; + static_assert(std::is_reference_v || !"triangle_refs named parameter must be a reference_wrapper"); + Triangle_refs triangle_refs = choose_parameter(get_parameter_reference(np, internal_np::triangle_refs), default_triangle_refs); + + std::vector default_edge_refs; + using Edge_refs = typename internal_np::Lookup_named_param_def>::reference; + static_assert(std::is_reference_v || !"edge_refs named parameter must be a reference_wrapper"); + Edge_refs edge_refs = choose_parameter(get_parameter_reference(np, internal_np::edge_refs), default_edge_refs); + + std::vector default_point_refs; + using Point_refs = typename internal_np::Lookup_named_param_def>::reference; + static_assert(std::is_reference_v || !"point_refs named parameter must be a reference_wrapper"); + Point_refs point_refs = choose_parameter(get_parameter_reference(np, internal_np::point_refs), default_point_refs); + + constexpr bool is_triangles_map = !parameters::is_default_parameter::value; + constexpr bool is_edges_map = !parameters::is_default_parameter::value; + constexpr bool is_ridges_map = !parameters::is_default_parameter::value; + constexpr bool is_corners_map = !parameters::is_default_parameter::value; + + constexpr bool is_tetrahedron_refs_map = !parameters::is_default_parameter::value; + constexpr bool is_triangle_refs_map = !parameters::is_default_parameter::value; + constexpr bool is_edge_refs_map = !parameters::is_default_parameter::value; + constexpr bool is_point_refs_map = !parameters::is_default_parameter::value; + bool is_CGAL_mesh; - return internal::read_MEDIT(is, points, finite_cells, subdomains, - border_facets, read_border_facets, - edge_indices, corner_indices, + return internal::read_MEDIT(is, points, tetrahedra, + triangles, is_triangles_map, + edges, is_edges_map, + ridges, is_ridges_map, + corners, is_corners_map, + point_refs, is_point_refs_map, + tetrahedron_refs, is_tetrahedron_refs_map, + triangle_refs, is_triangle_refs_map, + edge_refs, is_edge_refs_map, verbose, is_CGAL_mesh); } + +/*! + * \ingroup PkgStreamSupportIoFuncsMEDIT + * + * \brief writes a soup of indexed cells using the \ref IOStreamMedit. + * + + * \note Currently, only tetrahedral cells are supported. + * + * \tparam PointRange a model of `ConstRange` whose value type is the point type + * + * \param os the output stream + * \param points points of the soup of cells + * + * \param np optional \ref bgl_namedparameters "Named Parameters" described below + * + * \cgalNamedParamsBegin + * + * \cgalParamNBegin{point_refs} + * \cgalParamDescription{a const reference wrapper of a container of integers that will be written by this function. + * It must have the same size as `points`, and each element in the container is a value associated to the point at the same position.} + * \cgalParamType{a `std::reference_wrapper` to a model of `SequenceContainer` with an integer type as value type.} + * \cgalParamDefault{The value `0` is written for each point} + * \cgalParamNEnd + * + * \cgalParamNBegin{tetrahedra} + * \cgalParamDescription{a const reference wrapper of a container of quadruples of integers that will be written by this function. + * Each element corresponds to a tetrahedron with vertices corresponding to the four integers.} + * \cgalParamType{a `std::reference_wrapper` to a model of `SequenceContainer` with a value type where the elements of the quadruples can be accessed with `std::get()`} + * \cgalParamDefault{tetrahedra are not written} + * \cgalParamNEnd + * + * \cgalParamNBegin{tetrahedron_refs} + * \cgalParamDescription{a const reference wrapper of a container of integers that will be written by this function. + * It must have the same size as `tetrahedra`, and each element in the container is a value associated to the tetrahedron at the same position.} + * \cgalParamType{a `std::reference_wrapper` to a model of `SequenceContainer` with an integer type as value type.} + * \cgalParamDefault{The value `0` is written for each tetrahedron} + * \cgalParamNEnd + * + * \cgalParamNBegin{triangles} + * \cgalParamDescription{a const reference wrapper of a container of triples of integers that will be written by this function. + * Each element corresponds to a triangle with vertices corresponding to the three integers.} + * \cgalParamType{a `std::reference_wrapper` to a model of `SequenceContainer` with a value type where the elements of the triples can be accessed with `std::get()`} + * \cgalParamDefault{triangles are not written} + * \cgalParamNEnd + * + * \cgalParamNBegin{triangle_refs} + * \cgalParamDescription{a const reference wrapper of a container of integers that will be written by this function. + * It must have the same size as `triangles`, and each element in the container is a value associated to the triangle at the same position.} + * \cgalParamType{a `std::reference_wrapper` to a model of `SequenceContainer` with an integer type as value type.} + * \cgalParamDefault{The value `0` is written for each triangle} + * \cgalParamNEnd + * + * \cgalParamNBegin{edges} + * \cgalParamDescription{a const reference wrapper of a container of pairs of integers that will be written by this function. + * Each element corresponds to an edge with vertices corresponding to the two integers.} + * \cgalParamType{a `std::reference_wrapper` to a model of `SequenceContainer` with a value type where the elements of the pairs can be accessed with `std::get()`} + * \cgalParamDefault{edges are not written} + * \cgalParamNEnd + * + * \cgalParamNBegin{edge_refs} + * \cgalParamDescription{a const reference wrapper of a container of integers that will be written by this function. + * It must have the same size as `edges`, and each element in the container is a value associated to the edge at the same position.} + * \cgalParamType{a `std::reference_wrapper` to a model of `SequenceContainer` with an integer type as value type.} + * \cgalParamDefault{The value `0` is written for each edge} + * \cgalParamNEnd + * + * \cgalParamNBegin{ridges} + * \cgalParamDescription{a const reference wrapper of a container of integers that will be written by this function. + * Each element in the container is an index in the sequence of edges.} + * \cgalParamType{a `std::reference_wrapper` to a model of `SequenceContainer` with an integer type as value type.} + * \cgalParamDefault{ridges are not written} + * \cgalParamNEnd + * + * \cgalParamNBegin{corners} + * \cgalParamDescription{a const reference wrapper of a container of integers that will be written by this function. + * Each element in the container is an index in the sequence of points.} + * \cgalParamType{a `std::reference_wrapper` to a model of `SequenceContainer` with an integer type as value type.} + * \cgalParamDefault{corners are not written} + * \cgalParamNEnd + * + * + * \cgalNamedParamsEnd + * + * \returns `true` if the writing was successful, `false` otherwise. + * + * \see \ref IOStreamMedit + */ +template +bool write_MEDIT(std::ostream& os, + const PointRange& points, + const CGAL_NP_CLASS& np = parameters::default_values() +#ifndef DOXYGEN_RUNNING + , std::enable_if_t::value>* = nullptr +#endif + ) + +{ + using parameters::choose_parameter; + using parameters::get_parameter; + using parameters::get_parameter_reference; + using parameters::is_default_parameter; + + if(!os) + return false; + + std::vector default_ridges, default_corners; + std::vector default_tetrahedron_refs, default_triangle_refs, default_edge_refs, default_point_refs; + std::vector> default_edges; + std::vector> default_facets; + std::vector> default_cells; + + using Cells = typename internal_np::Lookup_named_param_def>>::reference; + Cells cells = choose_parameter(get_parameter_reference(np, internal_np::tetrahedra), default_cells); + + using Facets = typename internal_np::Lookup_named_param_def>>::reference; + Facets facets = choose_parameter(get_parameter_reference(np, internal_np::triangles), default_facets); + + using Edges = typename internal_np::Lookup_named_param_def>>::reference; + Edges edges = choose_parameter(get_parameter_reference(np, internal_np::edges), default_edges); + + using Ridges = typename internal_np::Lookup_named_param_def>::reference; + Ridges ridges = choose_parameter(get_parameter_reference(np, internal_np::ridges), default_ridges); + + using corners = typename internal_np::Lookup_named_param_def>>::reference; + corners vertices = choose_parameter(get_parameter_reference(np, internal_np::corners), default_corners); + + using Tetrahedron_refs = typename internal_np::Lookup_named_param_def>::reference; + Tetrahedron_refs tetrahedron_refs = choose_parameter(get_parameter_reference(np, internal_np::tetrahedron_refs), default_tetrahedron_refs); + + using Triangle_refs = typename internal_np::Lookup_named_param_def>::reference; + Triangle_refs triangle_refs = choose_parameter(get_parameter_reference(np, internal_np::triangle_refs), default_triangle_refs); + + using Edge_refs = typename internal_np::Lookup_named_param_def>::reference; + Edge_refs edge_refs = choose_parameter(get_parameter_reference(np, internal_np::edge_refs), default_edge_refs); + + using Point_refs = typename internal_np::Lookup_named_param_def>::reference; + Point_refs point_refs = choose_parameter(get_parameter_reference(np, internal_np::point_refs), default_point_refs); + + constexpr bool is_tetrahedron_refs_map = !parameters::is_default_parameter::value; + constexpr bool is_triangle_refs_map = !parameters::is_default_parameter::value; + constexpr bool is_edge_refs_map = !parameters::is_default_parameter::value; + constexpr bool is_point_refs_map = !parameters::is_default_parameter::value; + + os << "MeshVersionFormatted 1\nDimension 3\n"; + + os << "Vertices\n"; + os << points.size() << "\n"; + for (std::size_t k = 0; k < points.size(); ++k){ + const auto& p = points[k]; + int ref = is_point_refs_map ? point_refs[k] : 0; + os << p << " " << ref << "\n"; + } + + os << "Tetrahedra\n"; + os << cells.size() << "\n"; + for (std::size_t k=0; k < cells.size(); ++k) + { + int ref = is_tetrahedron_refs_map ? tetrahedron_refs[k] : 0; + os << cells[k][0]+1 << " " + << cells[k][1]+1 << " " + << cells[k][2]+1 << " " + << cells[k][3]+1 << " " + << ref << "\n"; + } + + if(!edges.empty()){ + os << "Edges\n" << edges.size() << "\n"; + for(std::size_t k = 0; k < edges.size(); ++k){ + const auto& e = edges[k]; + int ref = is_edge_refs_map ? edge_refs[k] : 0; + os << internal::get_zero(e) +1 << " " << internal::get_one(e) + 1 << " " << ref << "\n"; + } + } + + if(!facets.empty()){ + os << "Triangles\n" << facets.size() << "\n"; + for(std::size_t k = 0; k < facets.size(); ++k){ + const auto& f = facets[k]; + int ref = is_triangle_refs_map ? triangle_refs[k]: 0; + os << std::get<0>(f) +1 << " " << std::get<1>(f) + 1 << " " << std::get<2>(f) + 1 << " " << ref << "\n"; + } + } + + if(!vertices.empty()){ + os << "Corners\n" << vertices.size() << "\n"; + for(const auto& c : vertices) + os << c + 1 << "\n"; + } + + if(!ridges.empty()){ + os << "Ridges\n" << ridges.size() << "\n"; + for(const auto& r : ridges) + os << r + 1 << "\n"; + } + + os <<"End\n"; + return true; +} + } // namespace IO } // namespace CGAL diff --git a/Stream_support/include/CGAL/IO/OBJ.h b/Stream_support/include/CGAL/IO/OBJ.h index f19a8b51527..2affdaf65d4 100644 --- a/Stream_support/include/CGAL/IO/OBJ.h +++ b/Stream_support/include/CGAL/IO/OBJ.h @@ -306,7 +306,7 @@ bool read_OBJ(std::istream& is, * * \brief reads the content of `is` into `points` and `polygons`, using the \ref IOStreamOBJ. * - * \attention The polygon soup is not cleared, and the data from the stream are appended. + * \note The polygon soup is not cleared, and the data from the stream are appended. * * \tparam PointRange a model of the concept `BackInsertionSequence` whose value type is the point type * \tparam PolygonRange a model of the concept `BackInsertionSequence` whose `value_type` is @@ -356,7 +356,7 @@ bool read_OBJ(std::istream& is, * * \brief reads the content of the file `fname` into `points` and `polygons`, using the \ref IOStreamOBJ. * - * \attention The polygon soup is not cleared, and the data from the file are appended. + * \note The polygon soup is not cleared, and the data from the file are appended. * * \tparam PointRange a model of the concept `BackInsertionSequence` whose value type is the point type. * \tparam PolygonRange a model of the concept `BackInsertionSequence` whose `value_type` is diff --git a/Stream_support/include/CGAL/IO/PLY.h b/Stream_support/include/CGAL/IO/PLY.h index 8ac896815d2..d763a250964 100644 --- a/Stream_support/include/CGAL/IO/PLY.h +++ b/Stream_support/include/CGAL/IO/PLY.h @@ -274,9 +274,9 @@ bool read_PLY(std::istream& is, * * \brief reads the content of `is` into `points` and `polygons`, using the \ref IOStreamPLY. * - * \attention The polygon soup is not cleared, and the data from the stream are appended. + * \note The polygon soup is not cleared, and the data from the stream are appended. * - * \attention To read a binary file, the flag `std::ios::binary` must be set during the creation of the `ifstream`. + * \note To read a binary file, the flag `std::ios::binary` must be set during the creation of the `ifstream`. * * \tparam PointRange a model of the concept `BackInsertionSequence` whose value type is the point type * \tparam PolygonRange a model of the concept `BackInsertionSequence` whose `value_type` is @@ -350,7 +350,7 @@ bool read_PLY(std::istream& is, * * \brief reads the content of `fname` into `points` and `polygons`, using the \ref IOStreamPLY. * - * \attention The polygon soup is not cleared, and the data from the file are appended. + * \note The polygon soup is not cleared, and the data from the file are appended. * * \tparam PointRange a model of the concept `BackInsertionSequence` whose value type is the point type. * \tparam PolygonRange a model of the concept `BackInsertionSequence` whose `value_type` is @@ -432,7 +432,7 @@ bool read_PLY(const std::filesystem::path& fname, * * \brief writes the content of `points` and `polygons` in `out`, using the \ref IOStreamPLY. * - * \attention To write to a binary file, the flag `std::ios::binary` must be set during the creation + * \note To write to a binary file, the flag `std::ios::binary` must be set during the creation * of the `ofstream`, and the \link PkgStreamSupportEnumRef `IO::Mode` \endlink * of the stream must be set to `BINARY`. * @@ -645,7 +645,7 @@ make_ply_normal_reader(VectorMap normal_map); second element of the tuple should be a functor that constructs the value type of `PropertyMap` from N objects of types `T`. - \attention To read a binary file, the flag `std::ios::binary` must be set during the creation of the `ifstream`. + \note To read a binary file, the flag `std::ios::binary` must be set during the creation of the `ifstream`. \tparam OutputIteratorValueType type of objects that can be put in `PointOutputIterator`. It must be a model of `DefaultConstructible` and defaults to `value_type_traits::%type`. @@ -676,7 +676,7 @@ bool read_PLY_with_properties(std::istream& is, Potential additional point properties and faces are ignored. - \attention To read a binary file, the flag `std::ios::binary` must be set during the creation of the `ifstream`. + \note To read a binary file, the flag `std::ios::binary` must be set during the creation of the `ifstream`. \tparam OutputIteratorValueType type of objects that can be put in `PointOutputIterator`. It must be a model of `DefaultConstructible` and defaults to `value_type_traits::%type`. @@ -840,7 +840,7 @@ bool read_PLY(const std::filesystem::path& fname, be provided for `PropertyMap::value_type` that handles both ASCII and binary output (see `CGAL::IO::get_mode()`). - \attention To write to a binary file, the flag `std::ios::binary` must be set during the creation + \note To write to a binary file, the flag `std::ios::binary` must be set during the creation of the `ofstream`, and the \link PkgStreamSupportEnumRef `IO::Mode` \endlink of the stream must be set to `BINARY`. @@ -868,7 +868,7 @@ template #include #include +#include #include +#include +#include +#include typedef CGAL::Simple_cartesian Kernel; typedef Kernel::Point_3 Point_3; int main() { - const std::filesystem::path filename = CGAL::data_file_path("meshes/elephant.mesh"); + const std::string filename = "./data/polyhedral_complex.mesh"; std::ifstream input(filename); std::vector points; - std::vector subdomains; std::vector> cells; - bool verbose = false; - bool success = CGAL::IO::read_MEDIT(input, points, cells, subdomains, verbose); + std::vector> facets; + std::vector> edges; + std::vector ridges; + std::deque corners; + std::vector point_refs, cell_refs, facet_refs, edge_refs; + + bool success = CGAL::IO::read_MEDIT(input, points, CGAL::parameters::point_refs(std::ref(point_refs)) + .tetrahedra(std::ref(cells)) + .tetrahedron_refs(std::ref(cell_refs)) + .triangles(std::ref(facets)) + .triangle_refs(std::ref(facet_refs)) + .edges(std::ref(edges)) + .edge_refs(std::ref(edge_refs)) + .ridges(std::ref(ridges)) + .corners(std::ref(corners)) + .verbose(false)); + assert(success); + + std::ostringstream output; + output.precision(17); + CGAL::IO::write_MEDIT(output, points, CGAL::parameters::point_refs(std::cref(point_refs)) + .tetrahedra(std::cref(cells)) + .tetrahedron_refs(std::cref(cell_refs)) + .triangles(std::cref(facets)) + .triangle_refs(std::cref(facet_refs)) + .edges(std::cref(edges)) + .edge_refs(std::cref(edge_refs)) + .ridges(std::cref(ridges)) + .corners(std::cref(corners)) + + + ); + + std::istringstream input_bis(output.str()); + std::vector points_bis; + std::vector> cells_bis; + std::vector> facets_bis; + std::vector> edges_bis; + std::vector ridges_bis; + std::deque corners_bis; + std::vector point_refs_bis, cell_refs_bis, facet_refs_bis, edge_refs_bis; + + success = CGAL::IO::read_MEDIT(input_bis, points_bis, CGAL::parameters::point_refs(std::ref(point_refs_bis)) + .tetrahedra(std::ref(cells_bis)) + .tetrahedron_refs(std::ref(cell_refs_bis)) + .triangles(std::ref(facets_bis)) + .triangle_refs(std::ref(facet_refs_bis)) + .edges(std::ref(edges_bis)) + .edge_refs(std::ref(edge_refs_bis)) + .ridges(std::ref(ridges_bis)) + .corners(std::ref(corners_bis)) + .verbose(false)); + + assert(points== points_bis); + assert(cells == cells_bis); + assert(facets == facets_bis); + assert(edges == edges_bis); + assert(ridges == ridges_bis); + assert(corners == corners_bis); + assert(cell_refs == cell_refs_bis); + assert(facet_refs == facet_refs_bis); + assert(edge_refs == edge_refs_bis); + assert(point_refs == point_refs_bis); assert(success); + std::cout << "done" << std::endl; return 0; }