diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8b5cd59757de2780c15566396d31176fe6da3d56 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,65 @@ +# cmake ../ -DCMAKE_BUILD_TYPE=Debug + +cmake_minimum_required(VERSION 3.1) +project(Linear_cell_complex_Demo) + +if(NOT POLICY CMP0070 AND POLICY CMP0053) + # Only set CMP0053 to OLD with CMake<3.10, otherwise there is a warning. + cmake_policy(SET CMP0053 OLD) +endif() +if(POLICY CMP0071) + cmake_policy(SET CMP0071 NEW) +endif() + +# Find includes in corresponding build directories +set(CMAKE_INCLUDE_CURRENT_DIR ON) +# Instruct CMake to run moc automatically when needed. +set(CMAKE_AUTOMOC ON) +################################################################################ +add_definitions("-Wall -Wextra") +set(CMAKE_CXX_STANDARD 17) + +add_definitions("-g") # for profilling with vtune +add_link_options("-g") + +## For profilling with gprof +# add_definitions("-pg") +# add_link_options("-pg") +################################################################################ +find_package(CGAL REQUIRED COMPONENTS Qt5) +find_package(Qt5 REQUIRED COMPONENTS Script OpenGL Svg) + +add_definitions(-DCGAL_USE_BASIC_VIEWER -DQT_NO_KEYWORDS) + +## To add expensive tests +# add_definitions("-DCGAL_CHECK_EXPENSIVE") +################################################################################ +find_package(ASSIMP) +if (ASSIMP_FOUND) + ADD_DEFINITIONS(-DWITH_ASSIMP) +endif (ASSIMP_FOUND) +################################################################################ +include_directories(tools) +include_directories(BEFORE SYSTEM ${CMAKE_SOURCE_DIR}) + +set(src_tools + + cmap_copy.h + cmap_isomorphisms.h + cmap_signature.h + ) +add_library(lcc_tools_lib SHARED ${src_tools}) +target_link_libraries(lcc_tools_lib PUBLIC + CGAL::CGAL + CGAL::CGAL_Qt5 + tet) +if (ASSIMP_FOUND) + target_link_libraries(lcc_tools_lib PUBLIC assimp) +endif (ASSIMP_FOUND) +################################################################################ +add_executable(hexa-subdivision hexa-subdivision.cpp ${src_tools}) +target_link_libraries(hexa-subdivision PUBLIC lcc_tools_lib) +################################################################################ +add_executable(tetra-to-hexa tetra-to-hexa.cpp ${src_tools}) +target_link_libraries(tetra-to-hexa PUBLIC lcc_tools_lib) +################################################################################ diff --git a/src/Compute_stats.h b/src/Compute_stats.h new file mode 100644 index 0000000000000000000000000000000000000000..27ee8151dbe7017d9b167550f1ba89cc7ca2a88c --- /dev/null +++ b/src/Compute_stats.h @@ -0,0 +1,190 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// + +#ifndef COMPUTE_STATS_H +#define COMPUTE_STATS_H + +#include "Element_topo.h" +#include "lcc_convexity_test.h" +#include "One_info.h" +#include "Orientation.h" +#include "Volume_computation.h" +#include <iostream> + +/////////////////////////////////////////////////////////////////////////////// +template<typename LCC> +void display_stats(LCC& lcc, bool with_geom_tests=false) +{ + lcc.display_characteristics(std::cout); + std::cout<<", valid="<<lcc.is_valid()<<std::endl; + + typename LCC::FT vol=0, sum_vol=0; + One_info<double> vol_tetra, vol_hexa, vol_prism, vol_pyramid, vol_generic; + std::size_t nb_0free=0, nb_1free=0, nb_2free=0, nb_3free=0, nb_face_3_free=0; + std::size_t nbvol=0, nbvolconvex=0; + cell_topo t; + typename LCC::Dart_handle sd; + + auto treated=lcc.get_new_mark(); + auto face_treated=lcc.get_new_mark(); + // std::cout<<"Volumes of each 3-cell:"<<std::endl; + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + if (lcc.template is_free<0>(it)) { ++nb_0free; } + if (lcc.template is_free<1>(it)) { ++nb_1free; } + if (lcc.template is_free<2>(it)) { ++nb_2free; } + if (lcc.template is_free<3>(it)) + { + ++nb_3free; + if (!lcc.is_marked(it, face_treated)) + { + ++nb_face_3_free; + lcc.template mark_cell<2>(it, face_treated); + } + } + else + { + if (!lcc.is_marked(it, face_treated)) + { lcc.template mark_cell<2>(it, face_treated); } + } + + if(!lcc.is_marked(it, treated)) + { + lcc.template mark_cell<3>(it, treated); + t=Get_cell_topo<LCC,3>::run(lcc, it, sd); + if(with_geom_tests) + { vol=signed_volume(lcc, it); } + switch(t) + { + case PRISM: + //std::cout<<" PRISM: "<<vol<<std::endl; + vol_prism.update(vol); + break; + case PYRAMID: + //std::cout<<" PYRAMID: "<<vol<<std::endl; + vol_pyramid.update(vol); + break; + case TETRAHEDRON: + //std::cout<<" TETRAHEDRON: "<<vol<<std::endl; + vol_tetra.update(vol); + break; + case HEXAHEDRON: + //std::cout<<" HEXAHEDRON: "<<vol<<std::endl; + vol_hexa.update(vol); + break; + case GENERIC_3D: + //std::cout<<" GENERIC_3D: "<<vol<<std::endl; + vol_generic.update(vol); + break; + default: + //std::cout<<" ERROR: unknown type: "<<t<<std::endl; + break; + } + if(with_geom_tests) + { + if (is_volume_convex(lcc, it)) { ++nbvolconvex; } + /*std::cout<<"TO DEBUG [display_stats]: vol="<<vol + <<" generic_vol="<<signed_volume_of_generic_cell(lcc, it)<<std::endl;*/ + sum_vol+=vol; + } + ++nbvol; + } + } + + std::cout<<"#prisms="<<vol_prism.number_of_elements() + <<" #pyramids="<<vol_pyramid.number_of_elements() + <<" #tetrahedra="<<vol_tetra.number_of_elements() + <<" #hexahedra="<<vol_hexa.number_of_elements() + <<" #generic="<<vol_generic.number_of_elements() + <<" (TOTAL="<<nbvol; + if(with_geom_tests) { std::cout<<", convex="<<nbvolconvex<<")"<<std::endl; } + else { std::cout<<")"<<std::endl; } + + std::cout<<"#darts free=("<<nb_0free<<", "<<nb_1free<<", "<<nb_2free + <<", "<<nb_3free<<") #faces 3-free="<<nb_face_3_free<<std::endl; + if(with_geom_tests) + { + std::cout<<"Volume of all the 3-cells: "<<sum_vol<<std::endl; + std::cout<<" hexa:"<<vol_hexa<<std::endl; + std::cout<<" tetra:"<<vol_tetra<<std::endl; + std::cout<<" prism:"<<vol_prism<<std::endl; + std::cout<<" pyramid:"<<vol_pyramid<<std::endl; + std::cout<<" generic:"<<vol_generic<<std::endl; + } + + lcc.free_mark(face_treated); + lcc.free_mark(treated); + + check_orientation(lcc, true); +} +/////////////////////////////////////////////////////////////////////////////// +template<typename LCC> +void display_volume(LCC& lcc, typename LCC::Dart_handle dh) +{ + typename LCC::Dart_handle sd; + cell_topo t=Get_cell_topo<LCC,3>::run(lcc, dh, sd); + switch(t) + { + case PRISM: + std::cout<<"PRISM:"; + break; + case PYRAMID: + std::cout<<"PYRAMID:"; + break; + case TETRAHEDRON: + std::cout<<"TETRAHEDRON:"; + break; + case HEXAHEDRON: + std::cout<<"HEXAHEDRON:"; + break; + case GENERIC_3D: + std::cout<<"GENERIC_3D:"; + break; + default: + std::cout<<" ERROR: unknown type: "<<t<<std::endl; + break; + } + std::vector<typename LCC::Point> points; + for(auto it=lcc.template one_dart_per_incident_cell<0,3>(sd).begin(), + itend=lcc.template one_dart_per_incident_cell<0,3>(sd).end(); it!=itend; ++it) + { points.push_back(lcc.point(it)); } + std::sort(points.begin(), points.end()); + bool first=true; + for(auto& pt: points) + { + if(!first) { std::cout<<"; "; } + else { std::cout<<"("; first=false; } + std::cout<<pt; + } + std::cout<<")"<<std::endl; +} +/////////////////////////////////////////////////////////////////////////////// +template<typename LCC> +void display_all_volumes(LCC& lcc) +{ + std::cout<<"*********************************************"<<std::endl; + for(auto it=lcc.template one_dart_per_cell<3>().begin(), + itend=lcc.template one_dart_per_cell<3>().end(); it!=itend; ++it) + { display_volume(lcc, it); } + std::cout<<"*********************************************"<<std::endl; +} +/////////////////////////////////////////////////////////////////////////////// +#endif // COMPUTE_STATS_H diff --git a/src/Element_topo.h b/src/Element_topo.h new file mode 100644 index 0000000000000000000000000000000000000000..5579a6642ce02db6092547db3a36a912d253bc6d --- /dev/null +++ b/src/Element_topo.h @@ -0,0 +1,137 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr>, +// Florence Zara <florence.zara@liris.cnrs.fr> +// + +#ifndef ELEMENT_TOPO_H +#define ELEMENT_TOPO_H + +#include "Prism_and_pyramid_creation.h" + +enum cell_topo + { + SQUARE = 0, + TRIANGLE = 1, + HEXAHEDRON = 2, + TETRAHEDRON = 3, + PRISM = 4, + PYRAMID = 5, + GENERIC_2D = 6, + GENERIC_3D = 7, + EDGE = 8, + NO_TYPE = -1 + }; + +/** + * @brief To get the type of dimD cell of the LCC of dimlcc dimension. + */ +template<typename LCC, unsigned int dim, unsigned int dimlcc=LCC::dimension> +struct Get_cell_topo +{ + static cell_topo run(LCC&, typename LCC::Dart_handle dh, + typename LCC::Dart_handle& starting_dart) + { + starting_dart=dh; + return NO_TYPE; + } +}; + +/** + * @brief To get the type associated of an edge. For now only one type. + */ +template<typename LCC, unsigned int dimlcc> +struct Get_cell_topo<LCC, 1, dimlcc> +{ + static cell_topo run(LCC&, typename LCC::Dart_handle it, + typename LCC::Dart_handle& starting_dart) + { + starting_dart=it; + return EDGE; + } +}; + +/** + * @brief To get the type of 2D cell of the LCC of dimlcc dimension. + */ +template<typename LCC, unsigned int dimlcc> +struct Get_cell_topo<LCC, 2, dimlcc> +{ + static cell_topo run(LCC& lcc, typename LCC::Dart_handle it, + typename LCC::Dart_handle& starting_dart) + { + starting_dart=it; + + if (lcc.is_face_combinatorial_polygon(it, 3)) + return TRIANGLE; + + else if (lcc.is_face_combinatorial_polygon(it, 4)) + return SQUARE; + + return GENERIC_2D; + } +}; + +/** + * @brief To get the type of 3D cell of the LCC of dimension 3. + */ +template<typename LCC> +struct Get_cell_topo<LCC, 3, 3> +{ + static cell_topo run(LCC& lcc, typename LCC::Dart_handle it, + typename LCC::Dart_handle& starting_dart) + { + starting_dart=it; + + if (lcc.is_volume_combinatorial_tetrahedron(it)) + return TETRAHEDRON; + + else if (lcc.is_volume_combinatorial_hexahedron(it)) + return HEXAHEDRON; + + // For non symetric object, we need to test all darts + for (auto itv=lcc.template darts_of_cell<3>(it).begin(), + itvend=lcc.template darts_of_cell<3>(it).end(); itv!=itvend; ++itv) + { + starting_dart=itv; + + if (is_volume_combinatorial_prism(lcc, itv)) + return PRISM; + + else if (is_volume_combinatorial_pyramid(lcc, itv)) + return PYRAMID; + } + + return GENERIC_3D; + } +}; + +template<typename LCC> +cell_topo get_cell_topo(LCC& lcc, typename LCC::Dart_handle it, + typename LCC::Dart_handle& starting_dart) +{ return Get_cell_topo<LCC, LCC::dimension>::run(lcc, it, starting_dart); } + +template<typename LCC> +cell_topo get_cell_topo(LCC& lcc, typename LCC::Dart_handle it) +{ + typename LCC::Dart_handle dummy; + return get_cell_topo(lcc, it, dummy); +} + +#endif // ELEMENT_TOPO_H diff --git a/src/MeshComplex_3InTriangulation_3_to_lcc.h b/src/MeshComplex_3InTriangulation_3_to_lcc.h new file mode 100644 index 0000000000000000000000000000000000000000..b14569d33f001db55e41acdded27eb1580e0b3b5 --- /dev/null +++ b/src/MeshComplex_3InTriangulation_3_to_lcc.h @@ -0,0 +1,163 @@ +// Copyright (c) 2011 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +//////////////////////////////////////////////////////////////////////////////// +#ifndef MESH_COMPLEX_3_IN_TRIANGULATION_3_TO_LCC_H +#define MESH_COMPLEX_3_IN_TRIANGULATION_3_TO_LCC_H + +#include <CGAL/assertions.h> +#include <unordered_map> +#include <CGAL/Weighted_point_3.h> + +namespace internal +{ +template<typename Point> +struct Get_point +{ + static const Point& run(const Point& p) + { return p; } +}; + +template<typename Kernel> +struct Get_point<CGAL::Weighted_point_3<Kernel> > +{ + static const typename Kernel::Point_3& run(const CGAL::Weighted_point_3<Kernel>& p) + { return p.point(); } +}; +} + +/** Convert a given Triangulation_3 into a 3D linear cell complex. + * @param alcc the used linear cell complex. + * @param atr the Triangulation_3. + * @param avol_to_dart a pointer to a std::map associating to each + * tetrahedron of atr a corresponding dart in alcc. Not used if nullptr. + * @return A dart incident to the infinite vertex. + */ +template < class LCC, class Triangulation > +typename LCC::Dart_handle import_from_complex_3_in_triangulation_3 +(LCC& alcc, const Triangulation &atr, +std::unordered_map<typename Triangulation::Cell_handle, + typename LCC::Dart_handle >* avol_to_dart=nullptr) +{ + CGAL_static_assertion( LCC::dimension>=3 && LCC::ambient_dimension==3 ); + + // Case of empty triangulations. + if (atr.triangulation().number_of_vertices()==0) return LCC::null_handle; + + // Check the dimension. + if (atr.triangulation().dimension()!=3) return LCC::null_handle; + CGAL_assertion(atr.is_valid()); + + typedef typename Triangulation::Vertex_handle TVertex_handle; + typedef typename Triangulation::Cell_iterator TCell_iterator; + + // Create vertices in the map and associate in a map + // TVertex_handle and vertices in the map. + std::unordered_map< TVertex_handle, typename LCC::Vertex_attribute_handle > TV; + for (auto itv=atr.triangulation().vertices_begin(); + itv!=atr.triangulation().vertices_end(); ++itv) + { + TV[itv]=alcc.create_vertex_attribute + (internal::Get_point<typename Triangulation::Point>::run(itv->point())); + } + + // Create the tetrahedron and create a map to link Cell_iterator + // and tetrahedron. + TCell_iterator it; + std::unordered_map<typename Triangulation::Cell_handle, typename LCC::Dart_handle> TC; + std::unordered_map<typename Triangulation::Cell_handle, typename LCC::Dart_handle>* + mytc = (avol_to_dart==nullptr?&TC:avol_to_dart); + + typename LCC::Dart_handle res=LCC::null_handle, dart=LCC::null_handle; + typename LCC::Dart_handle cur=LCC::null_handle, neighbor=LCC::null_handle; + + for (it=atr.cells_in_complex_begin(); it!=atr.cells_in_complex_end(); ++it) + { + if (it->vertex(0)!=atr.triangulation().infinite_vertex() && + it->vertex(1)!=atr.triangulation().infinite_vertex() && + it->vertex(2)!=atr.triangulation().infinite_vertex() && + it->vertex(3)!=atr.triangulation().infinite_vertex()) + { + assert(TV.count(it->vertex(0))==1); + assert(TV.count(it->vertex(1))==1); + assert(TV.count(it->vertex(2))==1); + assert(TV.count(it->vertex(3))==1); + + res=alcc.make_tetrahedron(TV[it->vertex(0)], + TV[it->vertex(1)], + TV[it->vertex(2)], + TV[it->vertex(3)]); + + if ( dart==LCC::null_handle ) + { dart=res; } + + for (unsigned int i=0; i<4; ++i) + { + assert(&*(it->neighbor(i)->neighbor(atr.triangulation().mirror_index(it, i)))==&*it); + assert(&*(it->neighbor(i))!=&*it); + + switch (i) + { + case 0: cur=alcc.template opposite<2>(alcc.next(res)); break; + case 1: cur=alcc.template opposite<2>(alcc.previous(res)); break; + case 2: cur=alcc.template opposite<2>(res); break; + case 3: cur=res; break; + } + + auto maptcell_it=mytc->find(it->neighbor(i)); + if (maptcell_it!=mytc->end()) + { + switch (atr.triangulation().mirror_index(it,i) ) + { + case 0: neighbor=alcc.template opposite<2>(alcc.next(maptcell_it->second)); + break; + case 1: neighbor=alcc.template opposite<2>(alcc.previous(maptcell_it->second)); + break; + case 2: neighbor=alcc.template opposite<2>(maptcell_it->second); break; + case 3: neighbor=maptcell_it->second; break; + } + + while (alcc.vertex_attribute(neighbor)!= + alcc.vertex_attribute(alcc.other_extremity(cur))) + { neighbor = alcc.next(neighbor); } + + assert(alcc.vertex_attribute(alcc.other_extremity(cur))== + alcc.vertex_attribute(alcc.other_orientation(neighbor))); + assert(alcc.vertex_attribute(alcc.other_extremity(alcc.next(cur)))== + alcc.vertex_attribute(alcc.other_orientation(alcc.previous(neighbor)))); + assert(alcc.vertex_attribute(alcc.other_extremity(alcc.previous(cur)))== + alcc.vertex_attribute(alcc.other_orientation(alcc.next(neighbor)))); + assert(alcc.template is_free<3>(cur)); + assert(alcc.template is_free<3>(alcc.other_orientation(neighbor))); + + alcc.template topo_sew<3>(cur, alcc.other_orientation(neighbor)); + } + } + (*mytc)[it]=res; + } + } + CGAL_assertion(dart!=LCC::null_handle); + alcc.correct_invalid_attributes(); // Necessary to correct problem of two + // tetra adjacent by a vertex (non manifold case). + return dart; +} + +#endif // MESH_COMPLEX_3_IN_TRIANGULATION_3_TO_LCC_H +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/My_linear_cell_complex_incremental_builder.h b/src/My_linear_cell_complex_incremental_builder.h new file mode 100644 index 0000000000000000000000000000000000000000..f98a2996b0d765bdeca46af34c9b8e03d71a2331 --- /dev/null +++ b/src/My_linear_cell_complex_incremental_builder.h @@ -0,0 +1,570 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +#ifndef MY_LINEAR_CELL_COMPLEX_INCREMENTAL_BUILDER_H +#define MY_LINEAR_CELL_COMPLEX_INCREMENTAL_BUILDER_H 1 + +#include <vector> +#include <cstddef> +#include <unordered_map> +#include <initializer_list> +#include <CGAL/Linear_cell_complex_base.h> + +/////////////////////////////////////////////////////////////////////////////// +template<class LCC, class Combinatorial_data_structure= + typename LCC::Combinatorial_data_structure> +struct Add_vertex_to_face +{ + static typename LCC::Dart_handle run(LCC&, + typename LCC::Vertex_attribute_handle, + typename LCC::Dart_handle) + {} +}; +template<class LCC> +struct Add_vertex_to_face<LCC, CGAL::Combinatorial_map_tag> +{ + static typename LCC::Dart_handle run(LCC& lcc, + typename LCC::Vertex_attribute_handle vh, + typename LCC::Dart_handle prev_dart) + { + typename LCC::Dart_handle res=lcc.create_dart(vh); + if (prev_dart!=lcc.null_handle) + { + lcc.template link_beta<1>(prev_dart, res); + } + return res; + } + static void run_for_last(LCC&, + typename LCC::Vertex_attribute_handle, + typename LCC::Dart_handle) + { // here nothing to do, all darts were already created. + } +}; +template<class LCC> +struct Add_vertex_to_face<LCC, CGAL::Generalized_map_tag> +{ + static typename LCC::Dart_handle run(LCC& lcc, + typename LCC::Vertex_attribute_handle vh, + typename LCC::Dart_handle prev_dart) + { + typename LCC::Dart_handle res=lcc.create_dart(vh); + if (prev_dart!=lcc.null_handle) + { + lcc.template link_alpha<0>(prev_dart, res); + lcc.template link_alpha<1>(res, lcc.create_dart(vh)); + res=lcc.template alpha<1>(res); + } + return res; + } + static void run_for_last(LCC& lcc, + typename LCC::Vertex_attribute_handle vh, + typename LCC::Dart_handle prev_dart) + { + // here we need to create a last dart and 0-link it + assert(prev_dart!=lcc.null_handle); + lcc.template link_alpha<0>(prev_dart, lcc.create_dart(vh)); + } +}; +/////////////////////////////////////////////////////////////////////////////// +template<class LCC, class Combinatorial_data_structure= + typename LCC::Combinatorial_data_structure> +struct Find_opposite_2_no_control // No difference for CMap and GMap +{ + typedef typename LCC::Dart_handle DH; + typedef typename LCC::Vertex_attribute_handle VAH; + static DH run(LCC&, + std::unordered_map<VAH, std::unordered_map<VAH, DH>>& + vertex_to_dart_map_in_surface, + VAH vah1, VAH vah2) + { + // We are searching edge vah2->vah1 (the opposite of edge vah1->vah2) + auto it2=vertex_to_dart_map_in_surface.find(vah2); + if (it2!=vertex_to_dart_map_in_surface.end()) + { + auto it1=it2->second.find(vah1); + if (it1!=it2->second.end()) + { return it1->second; } + } + return nullptr; + } +}; +/////////////////////////////////////////////////////////////////////////////// +template<class LCC, class Combinatorial_data_structure= + typename LCC::Combinatorial_data_structure> +struct Find_opposite_2_with_control +{ + typedef typename LCC::Dart_handle DH; + typedef typename LCC::Vertex_attribute_handle VAH; + static DH run(LCC&, + std::unordered_map<VAH, std::unordered_map<VAH, DH>>&, + VAH, VAH) + { return nullptr; } +}; +template<class LCC> +struct Find_opposite_2_with_control<LCC, CGAL::Combinatorial_map_tag> +{ + typedef typename LCC::Dart_handle DH; + typedef typename LCC::Vertex_attribute_handle VAH; + static DH run(LCC& lcc, + std::unordered_map<VAH, std::unordered_map<VAH, DH>>& + vertex_to_dart_map_in_surface, + VAH vah1, VAH vah2) + { + DH res=Find_opposite_2_no_control<LCC>::run(lcc, + vertex_to_dart_map_in_surface, + vah1, vah2); + if (res!=nullptr) + { + if (!lcc.template is_free<2>(res)) + { // Here a dart vah1->vah2 already exists, and it was already 2-sewn. + std::cerr<<"ERROR in My_linear_cell_complex_incremental_builder_3: try to use a same oriented edge twice."<<std::endl; + return nullptr; + } + } + if (Find_opposite_2_no_control<LCC>::run(lcc, + vertex_to_dart_map_in_surface, + vah2, vah1)!=nullptr) + { // Here a dart vah1->vah2 already exists (but it was not already 2-sewn). + std::cerr<<"ERROR in My_linear_cell_complex_incremental_builder_3: try to use a same oriented edge twice."<<std::endl; + return nullptr; + } + + return res; + } +}; +template<class LCC> +struct Find_opposite_2_with_control<LCC, CGAL::Generalized_map_tag> +{ + typedef typename LCC::Dart_handle DH; + typedef typename LCC::Vertex_attribute_handle VAH; + static DH run(LCC& lcc, + std::unordered_map<VAH, std::unordered_map<VAH, DH>>& + vertex_to_dart_map_in_surface, + VAH vah1, VAH vah2) + { + DH res=Find_opposite_2_no_control<LCC>::run(lcc, + vertex_to_dart_map_in_surface, + vah1, vah2); + if (res!=nullptr) + { + if (!lcc.template is_free<2>(res)) + { // Here a dart vah1->vah2 already exists, and it was already 2-sewn. + std::cerr<<"ERROR in My_linear_cell_complex_incremental_builder_3: try to use a same oriented edge twice."<<std::endl; + return nullptr; + } + } + return res; + } +}; +/////////////////////////////////////////////////////////////////////////////// +template<class LCC, class Combinatorial_data_structure= + typename LCC::Combinatorial_data_structure> +struct Add_edge_in_associative_array +{ + typedef typename LCC::Dart_handle DH; + typedef typename LCC::Vertex_attribute_handle VAH; + static void run(LCC&, DH, + std::unordered_map<VAH, std::unordered_map<VAH, DH>>&) + {} +}; +template<class LCC> +struct Add_edge_in_associative_array<LCC, CGAL::Combinatorial_map_tag> +{ + typedef typename LCC::Dart_handle DH; + typedef typename LCC::Vertex_attribute_handle VAH; + static void run(LCC& lcc, DH dh, + std::unordered_map<VAH, std::unordered_map<VAH, DH>>& + vertex_to_dart_map_in_surface) + { + vertex_to_dart_map_in_surface[lcc.vertex_attribute(dh)].insert + (std::make_pair(lcc.vertex_attribute(lcc.next(dh)), dh)); + } +}; +template<class LCC> +struct Add_edge_in_associative_array<LCC, CGAL::Generalized_map_tag> +{ + typedef typename LCC::Dart_handle DH; + typedef typename LCC::Vertex_attribute_handle VAH; + static void run(LCC& lcc, DH dh, + std::unordered_map<VAH, std::unordered_map<VAH, DH>>& + vertex_to_dart_map_in_surface) + { + vertex_to_dart_map_in_surface[lcc.vertex_attribute(dh)].insert + (std::make_pair(lcc.vertex_attribute(lcc.template alpha<0>(dh)), dh)); + + vertex_to_dart_map_in_surface + [lcc.vertex_attribute(lcc.template alpha<0>(dh))].insert + (std::make_pair(lcc.vertex_attribute(dh), lcc.template alpha<0>(dh))); + } +}; +/////////////////////////////////////////////////////////////////////////////// +template<class LCC_, unsigned int dim=LCC_::dimension> +struct Sew3_for_LCC_incremental_builder +{ + static void run(LCC_& lcc, + typename LCC_::Dart_handle dh1, typename LCC_::Dart_handle dh2) + { + if(dh1!=nullptr) + { + if(!lcc.template is_free<3>(dh1)) + { + std::cerr<<"ERROR in My_linear_cell_complex_incremental_builder_3: " + <<"it exists more than 2 faces with same indices."<<std::endl; + } + else + { lcc.template sew<3>(lcc.other_orientation(dh1), dh2); } + } + } +}; +template<class LCC_> +struct Sew3_for_LCC_incremental_builder<LCC_, 2> +{ + static void run(LCC_&, typename LCC_::Dart_handle, typename LCC_::Dart_handle) + {} +}; +/////////////////////////////////////////////////////////////////////////////// +// Incremental builder +template < class LCC_ > +class My_linear_cell_complex_incremental_builder_3 +{ +public: + typedef LCC_ LCC; + typedef typename LCC::Dart_handle DH; + typedef typename LCC::Vertex_attribute_handle VAH; + typedef typename LCC::Point Point_3; + typedef typename LCC::size_type size_type; + + My_linear_cell_complex_incremental_builder_3(LCC & alcc) : + lcc(alcc) + {} + + VAH add_vertex(const Point_3& p) + { + VAH res=lcc.create_vertex_attribute(p); + vertex_map.push_back(res); + return res; + } + + void begin_facet() + { // std::cout<<"Begin facet: "<<std::flush; + first_dart=lcc.null_handle; + prev_dart =lcc.null_handle; + } + + void add_vertex_to_facet(size_type i) + { + CGAL_assertion(i<vertex_map.size()); + // std::cout<<i<<" "<<std::flush; + DH cur_dart=Add_vertex_to_face<LCC>::run(lcc, vertex_map[i], prev_dart); + if(prev_dart!=lcc.null_handle) + { + DH opposite=Find_opposite_2_with_control<LCC>:: + run(lcc, + vertex_to_dart_map_in_surface, + lcc.vertex_attribute(prev_dart), + lcc.vertex_attribute(cur_dart)); + if(opposite!=lcc.null_handle) + { + CGAL_assertion(lcc.template is_free<2>(opposite)); + lcc.template set_opposite<2>(prev_dart, opposite); + } + + Add_edge_in_associative_array<LCC>::run(lcc, prev_dart, + vertex_to_dart_map_in_surface); + + if (i<min_vertex) { min_vertex=i; min_dart=cur_dart; } + if (i>max_vertex) { max_vertex=i; } + } + else + { first_dart=cur_dart; min_vertex=max_vertex=i; min_dart=cur_dart; } + + prev_dart=cur_dart; + } + + // End of the facet. Return the first dart of this facet. + DH end_facet() + { + CGAL_assertion(first_dart!=lcc.null_handle && prev_dart!=lcc.null_handle); + + Add_vertex_to_face<LCC>::run_for_last(lcc, + lcc.vertex_attribute(first_dart), + prev_dart); + + lcc.set_next(prev_dart, first_dart); + + DH opposite=Find_opposite_2_with_control<LCC>:: + run(lcc, + vertex_to_dart_map_in_surface, + lcc.vertex_attribute(prev_dart), + lcc.vertex_attribute(first_dart)); + if(opposite!=lcc.null_handle) + { + CGAL_assertion(lcc.template is_free<2>(opposite)); + lcc.template set_opposite<2>(prev_dart, opposite); + } + + Add_edge_in_associative_array<LCC>::run(lcc, prev_dart, + vertex_to_dart_map_in_surface); + + if(LCC::dimension>2) + { + opposite=opposite_face(); + Sew3_for_LCC_incremental_builder<LCC>::run(lcc, opposite, min_dart); + add_face_in_array(); + } + return first_dart; + } + + DH add_facet(std::initializer_list<size_type> l) + { + begin_facet(); + for (std::size_t i:l) + { add_vertex_to_facet(i); } + return end_facet(); + } + + void begin_surface() + { + vertex_to_dart_map_in_surface.clear(); + } + + // End of the surface. Return one dart of the created surface. + DH end_surface() + { return first_dart; } + +protected: + /** test if the two given facets have the same vertex handle but with + * opposite orientations. For closed facets. + * @return true iff the two facets have the same vertex handle with opposite + * orientation. + */ + bool are_facets_opposite_and_same_vertex_handles(DH d1, DH d2) const + { + DH s1=d1; + DH s2=d2; + do + { + assert(lcc.is_next_exist(d1) && lcc.is_previous_exist(d2)); + assert(lcc.other_extremity(d2)!=lcc.null_handle); + + if (lcc.vertex_attribute(d1)!=lcc.vertex_attribute(d2)) + { return false; } + d1=lcc.next(d1); + d2=lcc.previous(d2); + } + while(d1!=s1); + + if (d2!=s2) { return false; } + return true; + } + + DH opposite_face() + { + auto it1=faces.find(min_vertex); + if(it1==faces.end()) { return nullptr; } + auto it2=it1->second.find(max_vertex); + if(it2==it1->second.end()) { return nullptr; } + for(auto it3=it2->second.begin(), it3end=it2->second.end(); it3!=it3end; ++it3) + { + if (are_facets_opposite_and_same_vertex_handles(*it3, min_dart)) + { return lcc.previous(*it3); } + } + return nullptr; + } + + void add_face_in_array() + { + faces[min_vertex][max_vertex].push_back(min_dart); + } + +private: + LCC& lcc; + std::vector<VAH> vertex_map; // Map each index to the corresponding vertex handle + + // A map to associate to each edge of a surface its dart. The edge is given + // by its two vertex handles (source-target). + std::unordered_map<VAH, std::unordered_map<VAH, DH>> vertex_to_dart_map_in_surface; + std::unordered_map<std::size_t, std::unordered_map<std::size_t, std::vector<DH>>> faces; + + DH first_dart; /// First dart of the current face + DH prev_dart; /// Prev dart of the current face + DH min_dart; /// dart with the min vertex of the current facet. + std::size_t min_vertex, max_vertex; /// min and max indices of vertices of the current face +}; +/////////////////////////////////////////////////////////////////////////////// +/* Create an hexahedron, given the indices of its vertices (in the following +* order), the vertex must already have been added in the incremental builder. +* 3 +* /|\ +* 0-|-2 +* \|/ +* 1 +*/ +template<typename IncrementalBuilder> +typename IncrementalBuilder::LCC::Dart_handle +make_tetrahedron_with_builder(IncrementalBuilder& ib, + std::size_t i0, + std::size_t i1, + std::size_t i2, + std::size_t i3) +{ + ib.begin_surface(); + ib.add_facet({i0,i1,i2}); + ib.add_facet({i1,i0,i3}); + ib.add_facet({i2,i1,i3}); + ib.add_facet({i0,i2,i3}); + return ib.end_surface(); +} +/////////////////////////////////////////////////////////////////////////////// +/* 4 + * /|\ + * 0-|-3 + * | | | + * 1---2 + */ +template<typename IncrementalBuilder> +typename IncrementalBuilder::LCC::Dart_handle + make_pyramid_with_builder(IncrementalBuilder& ib, + std::size_t i0, + std::size_t i1, + std::size_t i2, + std::size_t i3, + std::size_t i4) +{ + ib.begin_surface(); + ib.add_facet({i0,i1,i2,i3}); + ib.add_facet({i1,i0,i4}); + ib.add_facet({i2,i1,i4}); + ib.add_facet({i3,i2,i4}); + ib.add_facet({i0,i3,i4}); + return ib.end_surface(); +} +/////////////////////////////////////////////////////////////////////////////// +/* 3 + * /|\ + * 4---5 + * | | | + * | 0 | + * |/ \| + * 1---2 + */ +template<typename IncrementalBuilder> +typename IncrementalBuilder::LCC::Dart_handle + make_prism_with_builder(IncrementalBuilder& ib, + std::size_t i0, + std::size_t i1, + std::size_t i2, + std::size_t i3, + std::size_t i4, + std::size_t i5) +{ + ib.begin_surface(); + ib.add_facet({i0,i1,i2}); + ib.add_facet({i1,i0,i3,i4}); + ib.add_facet({i2,i1,i4,i5}); + ib.add_facet({i0,i2,i5,i3}); + ib.add_facet({i5,i4,i3}); + return ib.end_surface(); +} +/////////////////////////////////////////////////////////////////////////////// +/* 7----6 + * /| /| + * 4----5 | + * | 3--|-2 + * |/ |/ + * 0----1 + */ +template<typename IncrementalBuilder> +typename IncrementalBuilder::LCC::Dart_handle + make_hexahedron_with_builder(IncrementalBuilder& ib, + std::size_t i0, + std::size_t i1, + std::size_t i2, + std::size_t i3, + std::size_t i4, + std::size_t i5, + std::size_t i6, + std::size_t i7) +{ + ib.begin_surface(); + ib.add_facet({i0,i1,i2,i3}); + ib.add_facet({i1,i0,i4,i5}); + ib.add_facet({i2,i1,i5,i6}); + ib.add_facet({i3,i2,i6,i7}); + ib.add_facet({i0,i3,i7,i4}); + ib.add_facet({i7,i6,i5,i4}); + return ib.end_surface(); +} +/////////////////////////////////////////////////////////////////////////////// +template<typename IncrementalBuilder> +typename IncrementalBuilder::LCC::Dart_handle + make_pentagonal_prism_with_builder(IncrementalBuilder& ib, + std::size_t i0, + std::size_t i1, + std::size_t i2, + std::size_t i3, + std::size_t i4, + std::size_t i5, + std::size_t i6, + std::size_t i7, + std::size_t i8, + std::size_t i9) +{ + ib.begin_surface(); + ib.add_facet({i0,i1,i2,i3,i4}); + ib.add_facet({i1,i0,i5,i6}); + ib.add_facet({i2,i1,i6,i7}); + ib.add_facet({i3,i2,i7,i8}); + ib.add_facet({i4,i3,i8,i9}); + ib.add_facet({i0,i4,i9,i5}); + ib.add_facet({i9,i8,i7,i6,i5}); + return ib.end_surface(); +} +/////////////////////////////////////////////////////////////////////////////// +template<typename IncrementalBuilder> +typename IncrementalBuilder::LCC::Dart_handle + make_hexagonal_prism_with_builder(IncrementalBuilder& ib, + std::size_t i0, + std::size_t i1, + std::size_t i2, + std::size_t i3, + std::size_t i4, + std::size_t i5, + std::size_t i6, + std::size_t i7, + std::size_t i8, + std::size_t i9, + std::size_t i10, + std::size_t i11) +{ + ib.begin_surface(); + ib.add_facet({i0,i1,i2,i3,i4,i5}); + ib.add_facet({i1,i0,i6,i7}); + ib.add_facet({i2,i1,i7,i8}); + ib.add_facet({i3,i2,i8,i9}); + ib.add_facet({i4,i3,i9,i10}); + ib.add_facet({i5,i4,i10,i11}); + ib.add_facet({i0,i5,i11,i6}); + ib.add_facet({i11,i10,i9,i8,i7,i6}); + return ib.end_surface(); +} +/////////////////////////////////////////////////////////////////////////////// +#endif // MY_LINEAR_CELL_COMPLEX_INCREMENTAL_BUILDER_H // +// EOF // diff --git a/src/One_info.h b/src/One_info.h new file mode 100644 index 0000000000000000000000000000000000000000..d1755fcaec55136d8dfedc17a087eebe4308598f --- /dev/null +++ b/src/One_info.h @@ -0,0 +1,91 @@ +// compute_arrangement: a new method to compute arrangement of segments. +// Copyright (C) 2020 CNRS and LIRIS' Establishments (France). +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// + +#ifndef ONE_INFO_H +#define ONE_INFO_H + +#include <cstddef> +#include <ostream> + +/// To store min, max and sum of a given info +template<typename T> +class One_info +{ +public: + One_info(): m_min(static_cast<T>(0)), + m_max(static_cast<T>(0)), + m_sum(static_cast<T>(0)), + m_nb(0) + {} + + void update(T val) + { + if (m_nb==0) + { + m_min=val; + m_max=val; + m_sum=val; + } + else + { + if (val<m_min) m_min=val; + if (val>m_max) m_max=val; + m_sum+=val; + } + ++m_nb; + } + + T min() const + { return m_min; } + + T max() const + { return m_max; } + + double mean() const + { + if (m_nb==0) {return 0.; } + return static_cast<double>(m_sum)/static_cast<double>(m_nb); + } + + T sum() const + { return m_sum; } + + std::size_t number_of_elements() const + { return m_nb; } + + void reinit() + { + m_min=static_cast<T>(0); + m_max=static_cast<T>(0); + m_sum=static_cast<T>(0); + m_nb=0; + } + + friend std::ostream& operator<<(std::ostream& os, const One_info& info) + { + os<<info.mean()<<" ("<<info.min()<<", "<<info.max()<<")"; + return os; + } + +protected: + T m_min, m_max, m_sum; + std::size_t m_nb; +}; + +#endif // ONE_INFO_H diff --git a/src/Orientation.h b/src/Orientation.h new file mode 100644 index 0000000000000000000000000000000000000000..f20d082dda891a67f0a2a3abe2b1fe6903dc3a73 --- /dev/null +++ b/src/Orientation.h @@ -0,0 +1,285 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// + +#ifndef ORIENTATION_H +#define ORIENTATION_H + +#include <CGAL/Exact_predicates_exact_constructions_kernel.h> +#include <CGAL/Exact_predicates_inexact_constructions_kernel.h> +#include "Element_topo.h" +#include "Volume_computation.h" + +/* 3 + * /|\ + * 0-|-2 + * \|/ + * 1 + */ +template<typename Point> +bool is_positive_tetra(const Point& p0, const Point& p1, + const Point& p2, const Point& p3) +{ return CGAL::orientation(p0, p1, p2, p3)==CGAL::POSITIVE; } + +/* 4 + * /|\ + * 0-|-3 + * | | | + * 1---2 + */ +template<typename Point> +bool is_positive_pyramid(const Point& p0, const Point& p1, + const Point& p2, const Point& p3, + const Point& p4) +{ + CGAL_USE(p3); + assert(is_positive_tetra(p0, p1, p2, p4)==is_positive_tetra(p0, p2, p3, p4)); + return is_positive_tetra(p0, p1, p2, p4); +} + +/* 3 + * /|\ + * 4---5 + * | | | + * | 0 | + * |/ \| + * 1---2 + */ +template<typename Point> +bool is_positive_prism(const Point& p0, const Point& p1, + const Point& p2, const Point& p3, + const Point& p4, const Point& p5) +{ + CGAL_USE(p3); CGAL_USE(p4); + assert(is_positive_tetra(p0, p1, p2, p5)==is_positive_tetra(p1, p0, p3, p5)); + assert(is_positive_tetra(p0, p1, p2, p5)==is_positive_tetra(p4, p3, p5, p1)); + return is_positive_tetra(p0, p1, p2, p5); +} + +/* 7----6 + * /| /| + * 4----5 | + * | 3--|-2 + * |/ |/ + * 0----1 +*/ +template<typename Point> +bool is_positive_hexa(const Point& p0, const Point& p1, + const Point& p2, const Point& p3, + const Point& p4, const Point& p5, + const Point& p6, const Point& p7) +{ + CGAL_USE(p2); CGAL_USE(p5); CGAL_USE(p6); CGAL_USE(p7); + assert(is_positive_tetra(p0, p1, p3, p4)==is_positive_tetra(p3, p1, p2, p6)); + assert(is_positive_tetra(p0, p1, p3, p4)==is_positive_tetra(p6, p5, p4, p1)); + assert(is_positive_tetra(p0, p1, p3, p4)==is_positive_tetra(p6, p4, p7, p3)); + assert(is_positive_tetra(p0, p1, p3, p4)==is_positive_tetra(p1, p3, p4, p6)); + return is_positive_tetra(p0, p1, p3, p4); +} + +template<typename LCC> +bool is_positive_generic_cell(LCC& lcc, typename LCC::Dart_handle dh) +{ + // Is is possible to do better? (since virtual tetra are not necessarily correct + // geometrically...) + return signed_volume_of_generic_cell(lcc,dh)>0; +} + +/// @return test if the 3-cell containing dg has positive orientation +template<typename LCC> +bool is_positive(LCC& lcc, typename LCC::Dart_handle dh) +{ + typename LCC::Dart_handle sd,d2; + cell_topo celltopo = Get_cell_topo<LCC, 3>::run(lcc, dh, sd); + + switch(celltopo) + { + case TETRAHEDRON: + return is_positive_tetra(lcc.point(sd), + lcc.point(lcc.template beta<1>(sd)), + lcc.point(lcc.template beta<0>(sd)), + lcc.point(lcc.template beta<2, 0>(sd))); + case HEXAHEDRON: + d2=lcc.template beta<2, 1, 1, 2, 1>(sd); + return is_positive_hexa(lcc.point(sd), + lcc.point(lcc.template beta<1>(sd)), + lcc.point(lcc.template beta<1,1>(sd)), + lcc.point(lcc.template beta<0>(sd)), + lcc.point(d2), + lcc.point(lcc.template beta<0>(d2)), + lcc.point(lcc.template beta<0,0>(d2)), + lcc.point(lcc.template beta<1>(d2))); + case PRISM: + d2=lcc.template beta<2, 1, 1, 2>(sd); + return is_positive_prism(lcc.point(sd), + lcc.point(lcc.template beta<1>(sd)), + lcc.point(lcc.template beta<0>(sd)), + lcc.point(lcc.template beta<1>(d2)), + lcc.point(d2), + lcc.point(lcc.template beta<0>(d2))); + case PYRAMID: + return is_positive_pyramid(lcc.point(sd), + lcc.point(lcc.template beta<1>(sd)), + lcc.point(lcc.template beta<1,1>(sd)), + lcc.point(lcc.template beta<0>(sd)), + lcc.point(lcc.template beta<2,0>(sd))); + case GENERIC_3D: + return is_positive_generic_cell(lcc, dh); + default: + std::cerr<<"Error in is_positive"<<std::endl; + } + return false; +} + +template<typename LCC> +bool check_orientation(LCC& lcc, bool messages=false) +{ + std::size_t nbplus=0, nbminus=0; + typename LCC::Dart_handle sd; + bool res=true; + + auto treated_cc=lcc.get_new_mark(); + auto treated_volume=lcc.get_new_mark(); + // std::cout<<"Volumes of each 3-cell:"<<std::endl; + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + if(!lcc.is_marked(it, treated_cc)) + { + std::size_t nbplus_cc=0, nbminus_cc=0; + for(auto itcc=lcc.template darts_of_cell_basic<4>(it, treated_cc).begin(), + itccend=lcc.template darts_of_cell_basic<4>(it, treated_cc).end(); + itcc!=itccend; ++itcc) + { + lcc.mark(itcc, treated_cc); // Normally not necessary, but safer + if(!lcc.is_marked(itcc, treated_volume)) + { + lcc.template mark_cell<3>(itcc, treated_volume); + if(is_positive(lcc, it)) { ++nbplus_cc; } + else { ++nbminus_cc; } + } + } + if(nbplus_cc!=0 && nbminus_cc!=0) + { + if(messages) + { + std::cout<<"[check_orientation]: INCORRECT ORIENTATION: one cc has " + <<nbplus_cc<<" positive volumes and "<<nbminus_cc + <<" negative ones."<<std::endl; + } + res=false; + } + nbplus+=nbplus_cc; nbminus+=nbminus_cc; + } + } + + if(messages) + { + if(nbplus==0) + { + if(nbminus==0) + { std::cout<<"[check_orientation]: empty map."<<std::endl; } + else + { + std::cout<<"[check_orientation]: all the "<<nbminus + <<" volumes have negative orientations."<<std::endl; + } + } + else + { + if(nbminus==0) + { + std::cout<<"[check_orientation]: all the "<<nbplus + <<" volumes have positive orientations."<<std::endl; + } + else + { + std::cout<<"[check_orientation]: INCORRECT ORIENTATION: " + <<nbplus<<" positive volumes and "<<nbminus + <<" negative ones."<<std::endl; + } + } + } + if (nbplus!=0 && nbminus!=0) { res=false; } + + lcc.free_mark(treated_cc); + lcc.free_mark(treated_volume); + return res; +} + +/// Reorient all the 3-cells to be positive (if true) or negative (if false) +/// @return true iff at least one cell was reorient, +/// false otherwise (the mesh has already the correct orientation) +/// @warning this method does not work if the lcc has no 3-boundary. Indeed, +/// in such a case, the infinite volume of a cc has necessarily the +/// opposite orientation of all the finite volumes in the same cc!! +template<typename LCC> +bool reorient_all(LCC& lcc, bool positive=true) +{ + typename LCC::Dart_handle sd; + bool res=false; + bool error=false; + + auto treated_cc=lcc.get_new_mark(); + auto treated_volume=lcc.get_new_mark(); + // std::cout<<"Volumes of each 3-cell:"<<std::endl; + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + if(!lcc.is_marked(it, treated_cc)) + { + std::size_t nbplus_cc=0, nbminus_cc=0; + for(auto itcc=lcc.template darts_of_cell_basic<4>(it, treated_cc).begin(), + itccend=lcc.template darts_of_cell_basic<4>(it, treated_cc).end(); + itcc!=itccend; ++itcc) + { + lcc.mark(itcc, treated_cc); // Normally not necessary, but safer + if(!lcc.is_marked(itcc, treated_volume)) + { + lcc.template mark_cell<3>(itcc, treated_volume); + if(is_positive(lcc, it)) { ++nbplus_cc; } + else { ++nbminus_cc; } + } + } + if(nbplus_cc!=0 && nbminus_cc!=0) + { + if(!error) + { + std::cout<<"[reorient_all]: INCORRECT ORIENTATION: one cc has " + <<nbplus_cc<<" positive volumes and "<<nbminus_cc + <<" negative ones. It is not possible to directly reorient " + <<"the mesh."<<std::endl; + error=true; + } + } + else if ((nbminus_cc!=0 && positive) || (nbplus_cc!=0 && !positive)) + { + lcc.reverse_orientation_connected_component(it); + res=true; + } + } + } + + assert(check_orientation(lcc, false)); + + lcc.free_mark(treated_cc); + lcc.free_mark(treated_volume); + return res; +} + +#endif // ORIENTATION_H diff --git a/src/Print_txt.h b/src/Print_txt.h new file mode 100644 index 0000000000000000000000000000000000000000..416ee448469a711d5eaa18b58e2dd4f1526a04c7 --- /dev/null +++ b/src/Print_txt.h @@ -0,0 +1,44 @@ +#ifndef PRINT_TXT_H +#define PRINT_TXT_H + +#include <sstream> +#include <utility> +#include <chrono> +#include <iostream> + +/////////////////////////////////////////////////////////////////////////////// +template<typename T> +void put_one_value(std::ostringstream& txt, const T& v) +{ txt<<v; } +//---------------------------------------------------------------------------- +template<> inline +void put_one_value(std::ostringstream& txt, + const std::chrono::duration<double>& v) +{ + //std::chrono::seconds s=std::chrono::duration_cast<std::chrono::seconds>(v); + txt<<v.count(); +} +/////////////////////////////////////////////////////////////////////////////// +template <typename Arg, typename... Args> +void print_txt(Arg&& arg, Args&&... args) +{ + std::ostringstream txt; + put_one_value(txt, std::forward<Arg>(arg)); + using expander = int[]; + (void)expander{0, (void(put_one_value(txt, std::forward<Args>(args))),0)...}; + std::cout<<txt.str()<<std::flush; +} +/////////////////////////////////////////////////////////////////////////////// +template <typename Arg, typename... Args> +void print_txt_with_endl(Arg&& arg, Args&&... args) +{ + std::ostringstream txt; + put_one_value(txt, std::forward<Arg>(arg)); + using expander = int[]; + (void)expander{0, (void(put_one_value(txt, std::forward<Args>(args))),0)...}; + txt<<std::endl; + std::cout<<txt.str()<<std::flush; +} +/////////////////////////////////////////////////////////////////////////////// + +#endif // PRINT_TXT_H diff --git a/src/Prism_and_pyramid_creation.h b/src/Prism_and_pyramid_creation.h new file mode 100644 index 0000000000000000000000000000000000000000..b03f050e1522c0a2d149325f06e097041261274f --- /dev/null +++ b/src/Prism_and_pyramid_creation.h @@ -0,0 +1,357 @@ +// Copyright (c) 2011 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +//////////////////////////////////////////////////////////////////////////////// +#ifndef PRISM_AND_PYRAMID_CREATION_H +#define PRISM_AND_PYRAMID_CREATION_H + +/** Create a combinatorial prism from 2 triangles and 3 squares. + * @param amap the used combinatorial map. + * @param d1 a dart onto a first triangle. + * @param d2 a dart onto a first square. + * @param d3 a dart onto a second square. + * @param d4 a dart onto a thirth square. + * @param d5 a dart onto a second triangle. + * @return a new dart. + */ + template < class Map > + typename Map::Dart_handle + make_combinatorial_prism(Map& amap, + typename Map::Dart_handle d1, + typename Map::Dart_handle d2, + typename Map::Dart_handle d3, + typename Map::Dart_handle d4, + typename Map::Dart_handle d5) + { + // 2-link for first triangle + amap.basic_link_beta_for_involution(d1, d2, 2); + amap.basic_link_beta_for_involution(amap.beta(d1, 1), d3, 2); + amap.basic_link_beta_for_involution(amap.beta(d1, 0), d4, 2); + + // 2-link for quandrangles between them + amap.basic_link_beta_for_involution(amap.beta(d2, 0), amap.beta(d3, 1), 2); + amap.basic_link_beta_for_involution(amap.beta(d2, 1), amap.beta(d4, 0), 2); + amap.basic_link_beta_for_involution(amap.beta(d3, 0), amap.beta(d4, 1), 2); + + // 2-link for second triangle + amap.basic_link_beta_for_involution(amap.beta(d2, 1, 1), d5, 2); + amap.basic_link_beta_for_involution(amap.beta(d3, 1, 1), amap.beta(d5, 0), 2); + amap.basic_link_beta_for_involution(amap.beta(d4, 1, 1), amap.beta(d5, 1), 2); + + return d1; + } + + /** Create a new combinatorial prism. + * @param amap the used combinatorial map. + * @return a new dart. + */ + template < class Map > + typename Map::Dart_handle make_combinatorial_prism(Map& amap) + { + typename Map::Dart_handle d1 = make_combinatorial_polygon(amap,3); + typename Map::Dart_handle d2 = make_combinatorial_polygon(amap,4); + typename Map::Dart_handle d3 = make_combinatorial_polygon(amap,4); + typename Map::Dart_handle d4 = make_combinatorial_polygon(amap,4); + typename Map::Dart_handle d5 = make_combinatorial_polygon(amap,3); + + return make_combinatorial_prism(amap, d1, d2, d3, d4, d5); + } + +/** Test if a volume is a combinatorial prism. + * @param amap the used combinatorial map. + * @param adart an intial dart + * @return true iff the volume containing adart is a combinatorial prism. + */ +template < class Map > +bool is_volume_combinatorial_prism(const Map& amap, + typename Map::Dart_const_handle d1) +{ + typename Map::Dart_const_handle d2 = amap.beta(d1, 2); + typename Map::Dart_const_handle d3 = amap.beta(d1, 1, 2); + typename Map::Dart_const_handle d4 = amap.beta(d1, 0, 2); + typename Map::Dart_const_handle d5 = amap.beta(d2, 1, 1, 2); + + if ( d1==amap.null_dart_handle || d2==amap.null_dart_handle || + d3==amap.null_dart_handle || d4==amap.null_dart_handle || + d5==amap.null_dart_handle ) return false; + + if (!amap.is_face_combinatorial_polygon(d1, 3) || + !amap.is_face_combinatorial_polygon(d2, 4) || + !amap.is_face_combinatorial_polygon(d3, 4) || + !amap.is_face_combinatorial_polygon(d4, 4) || + !amap.is_face_combinatorial_polygon(d5, 3)) return false; + + // TODO do better with marks. + if ( amap.template belong_to_same_cell<2,1>(d1, d2) || + amap.template belong_to_same_cell<2,1>(d1, d3) || + amap.template belong_to_same_cell<2,1>(d1, d4) || + amap.template belong_to_same_cell<2,1>(d1, d5) || + amap.template belong_to_same_cell<2,1>(d2, d3) || + amap.template belong_to_same_cell<2,1>(d2, d4) || + amap.template belong_to_same_cell<2,1>(d2, d5) || + amap.template belong_to_same_cell<2,1>(d3, d4) || + amap.template belong_to_same_cell<2,1>(d3, d5) || + amap.template belong_to_same_cell<2,1>(d4, d5)) + return false; + + if (amap.beta(d2,0,2) !=amap.beta(d3,1) || + amap.beta(d2,1,2) !=amap.beta(d4,0) || + amap.beta(d3,0,2) !=amap.beta(d4,1) || + amap.beta(d3,1,1,2)!=amap.beta(d5,0) || + amap.beta(d4,1,1,2)!=amap.beta(d5,1)) return false; + + return true; +} + +/** Create a combinatorial pyramid from 1 square and 4 triangles. + * @param amap the used combinatorial map. + * @param d1 a dart onto the square. + * @param d2 a dart onto a first triangle. + * @param d3 a dart onto a second triangle. + * @param d4 a dart onto a thirth triangle. + * @param d5 a dart onto a fourth triangle. + * @return a new dart. + */ + template < class Map > + typename Map::Dart_handle + make_combinatorial_pyramid(Map& amap, + typename Map::Dart_handle d1, + typename Map::Dart_handle d2, + typename Map::Dart_handle d3, + typename Map::Dart_handle d4, + typename Map::Dart_handle d5) + { + // 2-link for the square + amap.basic_link_beta_for_involution(d1, d2, 2); + amap.basic_link_beta_for_involution(amap.beta(d1, 1), d5, 2); + amap.basic_link_beta_for_involution(amap.beta(d1, 1, 1), d4, 2); + amap.basic_link_beta_for_involution(amap.beta(d1, 0), d3, 2); + + // 2-link for first triangle + amap.basic_link_beta_for_involution(amap.beta(d2, 1), amap.beta(d3, 0), 2); + amap.basic_link_beta_for_involution(amap.beta(d2, 0), amap.beta(d5, 1), 2); + + // 2-link for triangles between them + amap.basic_link_beta_for_involution(amap.beta(d5, 0), amap.beta(d4, 1), 2); + amap.basic_link_beta_for_involution(amap.beta(d4, 0), amap.beta(d3, 1), 2); + + return d1; + } + + /** Create a new combinatorial pyramid. + * @param amap the used combinatorial map. + * @return a new dart. + */ + template < class Map > + typename Map::Dart_handle make_combinatorial_pyramid(Map& amap) + { + typename Map::Dart_handle d1 = make_combinatorial_polygon(amap,4); + typename Map::Dart_handle d2 = make_combinatorial_polygon(amap,3); + typename Map::Dart_handle d3 = make_combinatorial_polygon(amap,3); + typename Map::Dart_handle d4 = make_combinatorial_polygon(amap,3); + typename Map::Dart_handle d5 = make_combinatorial_polygon(amap,3); + + return make_combinatorial_pyramid(amap, d1, d2, d3, d4, d5); + } + +/** Test if a volume is a combinatorial pyramid. + * @param amap the used combinatorial map. + * @param adart an intial dart + * @return true iff the volume containing adart is a combinatorial pyramid. + */ +template < class Map > +bool is_volume_combinatorial_pyramid(const Map& amap, + typename Map::Dart_const_handle d1) +{ + typename Map::Dart_const_handle d2 = amap.beta(d1, 2); + typename Map::Dart_const_handle d3 = amap.beta(d1, 0, 2); + typename Map::Dart_const_handle d4 = amap.beta(d1, 1, 1, 2); + typename Map::Dart_const_handle d5 = amap.beta(d1, 1, 2); + + if ( d1==amap.null_dart_handle || d2==amap.null_dart_handle || + d3==amap.null_dart_handle || d4==amap.null_dart_handle || + d5==amap.null_dart_handle ) return false; + + if (!amap.is_face_combinatorial_polygon(d1, 4) || + !amap.is_face_combinatorial_polygon(d2, 3) || + !amap.is_face_combinatorial_polygon(d3, 3) || + !amap.is_face_combinatorial_polygon(d4, 3) || + !amap.is_face_combinatorial_polygon(d5, 3)) return false; + + // TODO do better with marks. + if ( amap.template belong_to_same_cell<2,1>(d1, d2) || + amap.template belong_to_same_cell<2,1>(d1, d3) || + amap.template belong_to_same_cell<2,1>(d1, d4) || + amap.template belong_to_same_cell<2,1>(d1, d5) || + amap.template belong_to_same_cell<2,1>(d2, d3) || + amap.template belong_to_same_cell<2,1>(d2, d4) || + amap.template belong_to_same_cell<2,1>(d2, d5) || + amap.template belong_to_same_cell<2,1>(d3, d4) || + amap.template belong_to_same_cell<2,1>(d3, d5) || + amap.template belong_to_same_cell<2,1>(d4, d5)) + return false; + + if (amap.beta(d2,1,2)!=amap.beta(d3,0) || + amap.beta(d2,0,2)!=amap.beta(d5,1) || + amap.beta(d5,0,2)!=amap.beta(d4,1) || + amap.beta(d4,0,2)!=amap.beta(d3,1)) return false; + + return true; +} + +/** Create an prism given 6 Vertex_attribute_handle. + * (6 vertices, 9 edges and 5 facets) + * \verbatim + * 3---4 + * |\ /| + * 0-5-1 + * \|/ + * 2 + * \endverbatim + * @param h0 the first vertex handle. + * @param h1 the second vertex handle. + * @param h2 the third vertex handle. + * @param h3 the fourth vertex handle. + * @param h4 the fifth vertex handle. + * @param h5 the sixth vertex handle. + * @return the dart of the new prism incident to h0 and to + * the facet (h0,h1,h2). + */ + template < class Map > + typename Map::Dart_handle make_prism(Map& amap, + typename Map::Vertex_attribute_handle h0, + typename Map::Vertex_attribute_handle h1, + typename Map::Vertex_attribute_handle h2, + typename Map::Vertex_attribute_handle h3, + typename Map::Vertex_attribute_handle h4, + typename Map::Vertex_attribute_handle h5) + { + typename Map::Dart_handle d1 = amap.make_triangle(h0, h1, h2); + typename Map::Dart_handle d2 = amap.make_quadrangle(h1, h0, h3, h4); + typename Map::Dart_handle d3 = amap.make_quadrangle(h2, h1, h4, h5); + typename Map::Dart_handle d4 = amap.make_quadrangle(h0, h2, h5, h3); + typename Map::Dart_handle d5 = amap.make_triangle(h4, h3, h5); + + return make_combinatorial_prism(amap, d1, d2, d3, d4, d5); + } + + /** Create an prism given 6 points. + * \verbatim + * 3---4 + * |\ /| + * 0-5-1 + * \|/ + * 2 + * \endverbatim + * @param p0 the first point. + * @param p1 the second point. + * @param p2 the third point. + * @param p3 the fourth point. + * @param p4 the fifth point. + * @param p5 the sixth point. + * @return the dart of the new prism incident to p0 and to + * the facet (p0,p1,p2). + */ + template < class Map > + typename Map::Dart_handle make_prism(Map& amap, + const typename Map::Point& p0, + const typename Map::Point& p1, + const typename Map::Point& p2, + const typename Map::Point& p3, + const typename Map::Point& p4, + const typename Map::Point& p5) + { + return make_prism(amap, + amap.create_vertex_attribute(p0), + amap.create_vertex_attribute(p1), + amap.create_vertex_attribute(p2), + amap.create_vertex_attribute(p3), + amap.create_vertex_attribute(p4), + amap.create_vertex_attribute(p5)); + } + + /** Create an pyramid given 5 Vertex_attribute_handle. + * (5 vertices, 8 edges and 5 facets) + * \verbatim + * 4 + * /|\ + * 0-|-1 + * | | | + * 3---2 + * \endverbatim + * @param h0 the first vertex handle. + * @param h1 the second vertex handle. + * @param h2 the third vertex handle. + * @param h3 the fourth vertex handle. + * @param h4 the fifth vertex handle. + * @return the dart of the new pyramid incident to h0 and to + * the facet (h0,h1,h2,h3). + */ + template < class Map > + typename Map::Dart_handle make_pyramid(Map& amap, + typename Map::Vertex_attribute_handle h0, + typename Map::Vertex_attribute_handle h1, + typename Map::Vertex_attribute_handle h2, + typename Map::Vertex_attribute_handle h3, + typename Map::Vertex_attribute_handle h4) + { + typename Map::Dart_handle d1 = amap.make_quadrangle(h0, h1, h2, h3); + typename Map::Dart_handle d2 = amap.make_triangle(h1, h0, h4); + typename Map::Dart_handle d3 = amap.make_triangle(h0, h3, h4); + typename Map::Dart_handle d4 = amap.make_triangle(h3, h2, h4); + typename Map::Dart_handle d5 = amap.make_triangle(h2, h1, h4); + + return make_combinatorial_pyramid(amap, d1, d2, d3, d4, d5); + } + + /** Create an pyramid given 5 points. + * \verbatim + * 4 + * /|\ + * 0-|-1 + * | | | + * 3---2 + * \endverbatim + * @param p0 the first point. + * @param p1 the second point. + * @param p2 the third point. + * @param p3 the fourth point. + * @param p4 the fifth point. + * @return the dart of the new pyramid incident to p0 and to + * the facet (p0,p1,p2,p3). + */ + template < class Map > + typename Map::Dart_handle make_pyramid(Map& amap, + const typename Map::Point& p0, + const typename Map::Point& p1, + const typename Map::Point& p2, + const typename Map::Point& p3, + const typename Map::Point& p4) + { + return make_pyramid(amap, + amap.create_vertex_attribute(p0), + amap.create_vertex_attribute(p1), + amap.create_vertex_attribute(p2), + amap.create_vertex_attribute(p3), + amap.create_vertex_attribute(p4)); + } + +#endif // PRISM_AND_PYRAMID_CREATION_H +/////////////////////////////////////////////////////////////////////////////// diff --git a/src/Tetrahedral_tools.h b/src/Tetrahedral_tools.h new file mode 100644 index 0000000000000000000000000000000000000000..cb6bb81a9ae597b92ab9c3b82af546ac37058bb1 --- /dev/null +++ b/src/Tetrahedral_tools.h @@ -0,0 +1,251 @@ +// Copyright (c) 2011 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +//////////////////////////////////////////////////////////////////////////////// +#include <CGAL/Triangulation_3_to_lcc.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_3.h> +#include <CGAL/Surface_mesh.h> +#include <CGAL/make_mesh_3.h> +#include <CGAL/Polygon_mesh_processing/triangulate_faces.h> +#include <CGAL/Polygon_mesh_processing/measure.h> +#include "lcc_to_face_graph.h" +#include "lcc_to_tetgen_io.h" +#include "lcc_triangulate_faces.h" +#include "MeshComplex_3InTriangulation_3_to_lcc.h" +#include <tetgen.h> +//////////////////////////////////////////////////////////////////////////////// +/* Code trying to use CGAL mesher, problem to fix parameters. +template<typename LCC> +void tetrahedralize(LCC &lcc) +{ + typedef typename LCC::Traits K; + typedef typename CGAL::Surface_mesh<typename K::Point_3> Polyhedron; + typedef CGAL::Polyhedral_mesh_domain_3<Polyhedron, K> Mesh_domain; + typedef CGAL::Sequential_tag Concurrency_tag; // Or CGAL::Parallel_tag + typedef typename CGAL::Mesh_triangulation_3<Mesh_domain, CGAL::Default, + Concurrency_tag>::type Tr; + typedef CGAL::Mesh_criteria_3<Tr> Mesh_criteria; + typedef CGAL::Mesh_complex_3_in_triangulation_3<Tr> C3T3; + + Polyhedron p; + lcc_to_face_graph(lcc, p); + CGAL::Polygon_mesh_processing::triangulate_faces(p); + + double cursize, sumsize=0., minsize, maxsize; + std::size_t nbf=0; + for(typename boost::graph_traits<Polyhedron>::face_descriptor f: faces(p)) + { + auto he1=CGAL::halfedge(f, p); + auto he2=CGAL::next(he1, p); + auto he3=CGAL::next(he2, p); + cursize=std::sqrt(CGAL::squared_area(p.point(CGAL::source(he1, p)), + p.point(CGAL::source(he2, p)), + p.point(CGAL::source(he3, p)))); + sumsize+=cursize; + if (nbf==0 || cursize<minsize) { minsize=cursize; } + if (nbf==0 || cursize>maxsize) { maxsize=cursize; } + ++nbf; + } + + double myvol=CGAL::Polygon_mesh_processing::volume(p); + + std::cout<<"minsize="<<minsize<<" maxsize="<<maxsize<<" meansize=" + <<sumsize/nbf<<" myvol="<<myvol<<std::endl; + std::size_t nbcells=10000; // Wanted number of cells + + Mesh_domain domain(p); + using namespace CGAL::parameters; + double fs=10*sumsize/nbf, fd=fs/100., es=fs; + std::cout<<"make_mesh_3: facet_size="<<fs<<", edge_size="<<es + <<", facet_distance="<<fd<<std::endl; + + double s=pow((myvol*10 / * /nbcells * /)*6*sqrt(2), 1.0/3.0); + fs=s*s*sqrt(3)/4.; + fd=fs/10; + es=fs; + + std::cout<<"make_mesh_3: facet_size="<<fs<<", edge_size="<<es + <<", facet_distance="<<fd<<std::endl; + + Mesh_criteria criteria(cell_size=myvol, ///nbcells, + facet_size=fs, + edge_size=es, + facet_distance=fd);//facet_size=fs, edge_size=es, facet_distance=fd); + // cell_radius_edge_ratio=3, facet_angle=30); + //facet_angle=25, facet_size=0.15, facet_distance=0.008, + // cell_radius_edge_ratio=3); // TODO compute parameters from the mesh + + C3T3 c3t3 = CGAL::make_mesh_3<C3T3>(domain, criteria);//, manifold()); + / * edge_size = 8, + facet_angle = 25, facet_size = 0.1, facet_distance = 0.2, + cell_radius_edge_ratio = 3, cell_size = 0.1);* / + // CGAL::refine_mesh_3(c3t3, domain, criteria); + + lcc.clear(); + import_from_complex_3_in_triangulation_3(lcc, c3t3); +}*/ +//////////////////////////////////////////////////////////////////////////////// +inline void tetgen_options(tetgenbehavior& b) +{ + // Required option + b.plc=1; // '-p', 0. Tetrahedralizes a piecewise linear complex (PLC). + b.zeroindex=1; // '-z', 0. Numbers all output items starting from zero. + b.neighout=1; // '-n', 0. Outputs tetrahedra neighbors to .neigh file. + + // Required to be able to tetrahedralize only some volumes + b.nobisect=1; // '-Y', 0. Preserves the input surface mesh (does not modify it). + + // Optimisation + b.quality=1; // '-q', 0. Refines mesh (to improve mesh quality). + + // To debug + // b.diagnose=1; // '-d', 0. Detects self-intersections of facets of the PLC. + + /* + b.psc; // '-s', 0. + b.refine; // '-r', 0. Reconstructs a previously generated mesh. + b.coarsen; // '-R', 0. Mesh coarsening (to reduce the mesh elements). + b.weighted; // '-w', 0. Generates weighted Delaunay (regular) triangulation. + b.brio_hilbert; // '-b', 1. + b.incrflip; // '-l', 0. + b.flipinsert; // '-L', 0. + b.metric; // '-m', 0. Applies a mesh sizing function. + b.varvolume; // '-a', 0. Applies a maximum tetrahedron volume constraint. + b.fixedvolume; // '-a', 0. + b.regionattrib; // '-A', 0. Assigns attributes to tetrahedra in different regions. + b.conforming; // '-D', 0. + b.insertaddpoints; // '-i', 0. Inserts a list of additional points. + b.convex; // '-c', 0. Retains the convex hull of the PLC. + b.nomergefacet; // '-M', 0. No merge of coplanar facets or very close vertices. + b.nomergevertex; // '-M', 0. + b.noexact; // '-X', 0. Suppresses use of exact arithmetic. + b.nostaticfilter; // '-X', 0. + b.facesout; // '-f', 0. Outputs all faces to .face file. + b.edgesout; // '-e', 0. Outputs all edges to .edge file. + b.voroout; // '-v', 0. Outputs Voronoi diagram to files. + b.meditview; // '-g', 0. Outputs mesh to .mesh file for viewing by Medit. + b.vtkview; // '-k', 0. Outputs mesh to .vtk file for viewing by Paraview. + b.nobound; // '-B', 0. Suppresses output of boundary information. + b.nonodewritten; // '-N', 0. Suppresses output of .node file. + b.noelewritten; // '-E', 0. Suppresses output of .ele file. + b.nofacewritten; // '-F', 0. Suppresses output of .face and .edge file. + b.noiterationnum; // '-I', 0. Suppresses mesh iteration numbers. + b.nojettison; // '-J', 0. No jettison of unused vertices from output .node file. + b.reversetetori; // '-R', 0. + b.docheck; // '-C', 0. Checks the consistency of the final mesh. + b.quiet; // '-Q', 0. Quiet: No terminal output except errors. + b.verbose; // '-V', 0. Verbose: Detailed information, more terminal output. + + b.vertexperblock; // '-x', 4092. + b.tetrahedraperblock; // '-x', 8188. + b.shellfaceperblock; // '-x', 2044. + b.nobisect_param; // '-Y', 2. + b.addsteiner_algo; // '-Y/', 1. + b.coarsen_param; // '-R', 0. Mesh coarsening (to reduce the mesh elements). + b.weighted_param; // '-w', 0. Generates weighted Delaunay (regular) triangulation. + b.fliplinklevel; // -1. + b.flipstarsize; // -1. + b.fliplinklevelinc; // 1. + b.reflevel; // '-D', 3. + b.optlevel; // '-O', 2. Specifies the level of mesh optimization. + b.optscheme; // '-O', 7. + b.delmaxfliplevel; // 1. + b.order; // '-o', 1. + b.steinerleft; // '-S', 0. Specifies maximum number of added points. + b.no_sort; // 0. + b.hilbert_order; // '-b///', 52. + b.hilbert_limit; // '-b//' 8. + b.brio_threshold; // '-b' 64. + b.brio_ratio; // '-b/' 0.125. + b.facet_ang_tol; // '-p', 179.9. + b.maxvolume; // '-a', -1.0. Applies a maximum tetrahedron volume constraint. + b.minratio; // '-q', 0.0. + b.mindihedral; // '-q', 5.0. + b.optmaxdihedral; // 165.0. + b.optminsmtdihed; // 179.0. + b.optminslidihed; // 179.0. + b.epsilon; // '-T', 1.0e-8. Sets a tolerance for coplanar test (default 10−8). + b.minedgelength; // 0.0. + b.coarsen_percent; // -R1/#, 1.0. +*/ +} +//////////////////////////////////////////////////////////////////////////////// +/// Tetrahedralize all marked volumes of the lcc. +/// (a volume is considered marked if one its dart is marked) +/// At the end, no more dart is marked. +template <typename LCC> +void tetrahedralize_with_tetgen(LCC& lcc, typename LCC::size_type amark) +{ + // 1) Triangulate all marked faces + triangulate_marked_faces(lcc, amark); + + auto newdart=lcc.get_new_mark(); + lcc.negate_mark(newdart); // Old darts are marked + for(auto it=lcc.darts().begin(); it!=lcc.darts().end(); ++it) + { + if(lcc.is_marked(it, amark)) + { + // 2) Fill tetgen surface + tetgenio mysurface; + tetgenbehavior b; + lcc_to_tetgen_one_volume_(lcc, it, mysurface); + + // 3) Tetrahedralize + tetgenio result; + tetgen_options(b); + tetrahedralize(&b, &mysurface, &result); // char *switches, tetgenio *in, tetgenio *out, tetgenio *addin = NULL, tetgenio *bgmin = NULL + + // 4) Transfert tetra into lcc + lcc.template remove_cell<3>(it); + tetgen_to_lcc(result, lcc); + } + } + + lcc.negate_mark(newdart); // Now only new darts are marked + lcc.sew3_same_facets(newdart); // 3-sew faces of the new tetrahedra with old faces + + lcc.free_mark(newdart); +} +//////////////////////////////////////////////////////////////////////////////// +/// Tetrahedralize all volumes of the lcc. +template<typename LCC> +void tetrahedralize_with_tetgen(LCC &lcc) +{ + // 1) Triangulate all faces + triangulate_all_faces(lcc); + + // 2) Fill tetgen surface + tetgenio mysurface; + tetgenbehavior b; + lcc_to_tetgen(lcc, mysurface); + + // 3) Tetrahedralize + tetgenio result; + tetgen_options(b); + tetrahedralize(&b, &mysurface, &result); // char *switches, tetgenio *in, tetgenio *out, tetgenio *addin = NULL, tetgenio *bgmin = NULL + + // 4) Transfert tetra into lcc + lcc.clear(); + tetgen_to_lcc(result, lcc); +} +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Volume_computation.h b/src/Volume_computation.h new file mode 100644 index 0000000000000000000000000000000000000000..76efb8d215b87f8c8c0dda2d00474fc98890a955 --- /dev/null +++ b/src/Volume_computation.h @@ -0,0 +1,235 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// Florence Zara <florence.zara@liris.cnrs.fr> +// + +#ifndef VOLUME_COMPUTATION_H +#define VOLUME_COMPUTATION_H + +#include <CGAL/Exact_predicates_exact_constructions_kernel.h> +#include <CGAL/Exact_predicates_inexact_constructions_kernel.h> +#include <CGAL/Kernel_traits.h> +#include <CGAL/Origin.h> +#include <cmath> +#include "Element_topo.h" + +/*! Compute the area of the triangle p0, p1, p2 (points are 3D points). */ +template<typename Point> +typename Point::FT area_of_triangle(const Point& p0, + const Point& p1, + const Point& p2) +{ + typename CGAL::Kernel_traits<Point>::Kernel::Triangle_3 t(p0, p1, p2); + return std::sqrt(t.squared_area()); +} + + /* 3 + * /|\ + * 0-|-2 + * \|/ + * 1 + */ +template<typename Point> +typename Point::FT signed_volume_of_tetra(const Point& p0, const Point& p1, + const Point& p2, const Point& p3) +{ return CGAL::volume(p0, p1, p2, p3); } + +template<typename Point> +typename Point::FT volume_of_tetra(const Point& p0, const Point& p1, + const Point& p2, const Point& p3) +{ return std::abs(signed_volume_of_tetra(p0, p1, p2, p3)); } + +/* 4 + * /|\ + * 0-|-3 + * | | | + * 1---2 + */ +template<typename Point> +typename Point::FT signed_volume_of_pyramid(const Point& p0, const Point& p1, + const Point& p2, const Point& p3, + const Point& p4) +{ return signed_volume_of_tetra(p0, p1, p2, p4)+ + signed_volume_of_tetra(p0, p2, p3, p4); } + +template<typename Point> +typename Point::FT volume_of_pyramid(const Point& p0, const Point& p1, + const Point& p2, const Point& p3, + const Point& p4) +{ return std::abs(signed_volume_of_pyramid(p0, p1, p2, p3, p4)); } + +/* 3 + * /|\ + * 4---5 + * | | | + * | 0 | + * |/ \| + * 1---2 + */ +template<typename Point> +typename Point::FT signed_volume_of_prism(const Point& p0, const Point& p1, + const Point& p2, const Point& p3, + const Point& p4, const Point& p5) +{ + return signed_volume_of_tetra(p0, p1, p2, p5) + + signed_volume_of_tetra(p1, p0, p3, p5) + + signed_volume_of_tetra(p4, p3, p5, p1); } + +template<typename Point> +typename Point::FT volume_of_prism(const Point& p0, const Point& p1, + const Point& p2, const Point& p3, + const Point& p4, const Point& p5) +{ return std::abs(signed_volume_of_prism(p0, p1, p2, p3, p4, p5)); } + +/* 7----6 + * /| /| + * 4----5 | + * | 3--|-2 + * |/ |/ + * 0----1 +*/ +template<typename Point> +typename Point::FT signed_volume_of_hexa(const Point& p0, const Point& p1, + const Point& p2, const Point& p3, + const Point& p4, const Point& p5, + const Point& p6, const Point& p7) +{ + return signed_volume_of_tetra(p0, p1, p3, p4) + + signed_volume_of_tetra(p3, p1, p2, p6) + + signed_volume_of_tetra(p6, p5, p4, p1) + + signed_volume_of_tetra(p6, p4, p7, p3) + + signed_volume_of_tetra(p1, p3, p4, p6); +} + +template<typename Point> +typename Point::FT volume_of_hexa(const Point& p0, const Point& p1, + const Point& p2, const Point& p3, + const Point& p4, const Point& p5, + const Point& p6, const Point& p7) +{ return std::abs(signed_volume_of_hexa(p0, p1, p2, p3, p4, p5, p6, p7)); } + +template<typename LCC> +typename LCC::FT signed_volume_of_generic_cell(LCC& lcc, + typename LCC::Dart_handle dh) +{ + typename LCC::FT vol=0; + typename LCC::Point *p0, *p1, *p2; + typename LCC::Point p3; + typename LCC::Point p4=CGAL::ORIGIN; // Used instead barycenter of volume; it is supposed to work whatever the position of this point + typename LCC::Dart_handle dh2; + + // Iterate through one dart per face of the volume containing dh. + for (auto it=lcc.template one_dart_per_incident_cell<2,3,2>(dh).begin(), + itend=lcc.template one_dart_per_incident_cell<2,3,2>(dh).end(); + it!=itend; ++it) + { + dh2=lcc.template beta<1,1,1>(it); + if (dh2==it) + { // Triangle + p0=&(lcc.point(it)); + p1=&(lcc.point(lcc.template beta<1>(it))); + p2=&(lcc.point(lcc.template beta<0>(it))); + vol+=signed_volume_of_tetra(*p0, *p1, *p2, p4); + } + else if (lcc.template beta<1>(dh2)==it) + { // Square + p0=&(lcc.point(it)); + p1=&(lcc.point(lcc.template beta<1>(it))); + p2=&(lcc.point(lcc.template beta<1,1>(it))); + vol+=signed_volume_of_tetra(*p0, *p1, *p2, p4); + + p1=p2; + p2=&(lcc.point(lcc.template beta<0>(it))); + vol+=signed_volume_of_tetra(*p0, *p1, *p2, p4); + } + else + { // Generic face (>4 edges) + p3=lcc.template barycenter<2>(it); + typename LCC::Dart_handle cur=it; + p0=&(lcc.point(cur)); + do + { + cur=lcc.next(cur); + p1=&(lcc.point(cur)); + vol+=signed_volume_of_tetra(*p0, *p1, p3, p4); + p0=p1; + } + while(cur!=it); + } + } + return vol; +} + +template<typename LCC> +typename LCC::FT volume_of_generic_cell(LCC& lcc, + typename LCC::Dart_handle dh) +{ return std::abs(signed_volume_of_generic_cell(lcc, dh)); } + +/// @return the volume of the cell +template<typename LCC> +typename LCC::FT signed_volume(LCC& lcc, typename LCC::Dart_handle dh) +{ + typename LCC::Dart_handle sd,d2; + cell_topo celltopo = Get_cell_topo<LCC, 3>::run(lcc, dh, sd); + + switch(celltopo) + { + case TETRAHEDRON: + return signed_volume_of_tetra(lcc.point(sd), + lcc.point(lcc.template beta<1>(sd)), + lcc.point(lcc.template beta<0>(sd)), + lcc.point(lcc.template beta<2, 0>(sd))); + case HEXAHEDRON: + d2=lcc.template beta<2, 1, 1, 2, 1>(sd); + return signed_volume_of_hexa(lcc.point(sd), + lcc.point(lcc.template beta<1>(sd)), + lcc.point(lcc.template beta<1,1>(sd)), + lcc.point(lcc.template beta<0>(sd)), + lcc.point(d2), + lcc.point(lcc.template beta<0>(d2)), + lcc.point(lcc.template beta<0,0>(d2)), + lcc.point(lcc.template beta<1>(d2))); + case PRISM: + d2=lcc.template beta<2, 1, 1, 2>(sd); + return signed_volume_of_prism(lcc.point(sd), + lcc.point(lcc.template beta<1>(sd)), + lcc.point(lcc.template beta<0>(sd)), + lcc.point(lcc.template beta<1>(d2)), + lcc.point(d2), + lcc.point(lcc.template beta<0>(d2))); + case PYRAMID: + return signed_volume_of_pyramid(lcc.point(sd), + lcc.point(lcc.template beta<1>(sd)), + lcc.point(lcc.template beta<1,1>(sd)), + lcc.point(lcc.template beta<0>(sd)), + lcc.point(lcc.template beta<2,0>(sd))); + case GENERIC_3D: + return signed_volume_of_generic_cell(lcc, dh); + default: + std::cerr<<"Error in signed_volume"<<std::endl; + } + return 0; +} + +template<typename LCC> +typename LCC::FT volume(LCC& lcc, typename LCC::Dart_handle dh) +{ return std::abs(signed_volume(lcc, dh)); } + +#endif // VOLUME_COMPUTATION_H diff --git a/src/cmap_3close_cc.h b/src/cmap_3close_cc.h new file mode 100644 index 0000000000000000000000000000000000000000..64a79689650b68e90c0cd238834e6907edc3e357 --- /dev/null +++ b/src/cmap_3close_cc.h @@ -0,0 +1,68 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// + +#ifndef CMAP_3CLOSE_CC_H +#define CMAP_3CLOSE_CC_H + +#include <cstddef> + +/** 3-close a connected component + * @param dh a dart + * @return the number of new darts. + */ +template<typename LCC> +std::size_t close_cc_for_beta3(LCC& lcc, typename LCC::Dart_handle dh) +{ + std::size_t res=0; + typename LCC::Dart_handle d, d2; + + for (auto it=lcc.template darts_of_cell<3>(dh).begin(), + itend=lcc.template darts_of_cell<3>(dh).end(); it!=itend; ++it) + { + if(lcc.template is_free<3>(it)) + { + d=lcc.create_dart(); + ++res; + lcc.template link_beta_for_involution<3>(it, d); + + // Special cases for 0 and 1 + if (!lcc.template is_free<1>(it) && + !lcc.template is_free<3>(lcc.template beta<1>(it))) + { lcc.template link_beta<1>(lcc.template beta<1,3>(it),d); } + if (!lcc.template is_free<0>(it) && + !lcc.template is_free<3>(lcc.template beta<0>(it))) + { lcc.template link_beta<0>(lcc.template beta<0,3>(it),d); } + + d2=lcc.template beta<2>(it); + while (d2!=lcc.null_dart_handle && + !lcc.template is_free<2>(lcc.template beta<3>(d2))) + { d2=lcc.template beta<3, 2>(d2); } + if (d2!=lcc.null_dart_handle && !lcc.template is_free<3>(d2)) + { + lcc.template basic_link_beta_for_involution<2> + (lcc.template beta<3>(d2), d); + } + } + } + return res; +} + +#endif // CMAP_3CLOSE_CC_H diff --git a/src/cmap_copy.h b/src/cmap_copy.h new file mode 100644 index 0000000000000000000000000000000000000000..26f62ec88368ad4741251d859020739703fcbc6c --- /dev/null +++ b/src/cmap_copy.h @@ -0,0 +1,134 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// + +#ifndef CMAP_COPY_H +#define CMAP_COPY_H + +#include <unordered_map> +#include <CGAL/Combinatorial_map/internal/Combinatorial_map_copy_functors.h> + +/** Copy volume(dh) from lcc1 to lcc2. + * @return the number of new darts. + */ +template<unsigned int dim, typename CMap1, typename CMap2, + typename Converters, typename DartInfoConverter, typename PointConverter> +std::size_t copy_cell(CMap1& amap1, typename CMap1::Dart_handle dh, + CMap2& amap2, + std::unordered_map + <typename CMap1::Dart_handle, typename CMap2::Dart_handle>* + origin_to_copy, + std::unordered_map + <typename CMap1::Dart_handle, typename CMap2::Dart_handle>* + copy_to_origin, + const Converters& converters, + const DartInfoConverter& dartinfoconverter, + const PointConverter& pointconverter, + bool copy_perforated_darts=false, + typename CMap1::size_type mark_perforated=CMap1::INVALID_MARK) +{ + std::size_t res=0; + + std::unordered_map<typename CMap1::Dart_handle, typename CMap2::Dart_handle> local_dartmap; + if (origin_to_copy==nullptr) // Use local_dartmap if user does not provides its own unordered_map + { origin_to_copy=&local_dartmap; } + + typename CMap2::Dart_handle new_dart; + for(auto it=amap1.template darts_of_cell<dim>(dh).begin(), + itend=amap1.template darts_of_cell<dim>(dh).end(); it!=itend; ++it) + { + if (copy_perforated_darts || !amap1.is_perforated(it)) + { + new_dart=amap2.create_dart(); // , amap.get_marks(it)); + + if (mark_perforated!=CMap1::INVALID_MARK && amap1.is_perforated(it)) + { amap2.mark(new_dart, mark_perforated); } + + (*origin_to_copy)[it]=new_dart; + if(copy_to_origin!=nullptr) { (*copy_to_origin)[new_dart]=it; } + + CGAL::internal::Copy_dart_info_functor + <typename CMap1::Refs, typename CMap2::Refs, DartInfoConverter>::run + (static_cast<const typename CMap1::Refs&>(amap1), + static_cast<typename CMap2::Refs&>(amap2), + it, new_dart, dartinfoconverter); + } + } + + unsigned int min_dim=std::min(amap1.dimension, amap2.dimension); + + typename std::unordered_map<typename CMap1::Dart_handle, + typename CMap2::Dart_handle>::iterator + dartmap_iter, dartmap_iter_end=origin_to_copy->end(); + for (dartmap_iter=origin_to_copy->begin(); dartmap_iter!=dartmap_iter_end; + ++dartmap_iter) + { + for (unsigned int i=0; i<=min_dim; i++) + { + if (i!=dim && + !amap1.is_free(dartmap_iter->first,i) && + amap2.is_free(dartmap_iter->second,i)) + { + amap2.basic_link_beta(dartmap_iter->second, + (*origin_to_copy)[amap1.beta(dartmap_iter->first,i)], i); + } + } + } + + /** Copy attributes */ + for (dartmap_iter=origin_to_copy->begin(); dartmap_iter!=dartmap_iter_end; + ++dartmap_iter) + { + CMap2::Helper::template Foreach_enabled_attributes + <CGAL::internal::Copy_attributes_functor<typename CMap1::Refs, + typename CMap2::Refs, Converters, PointConverter>>:: + run(static_cast<const typename CMap1::Refs&>(amap1), + static_cast<typename CMap2::Refs&>(amap2), + dartmap_iter->first, dartmap_iter->second, converters, pointconverter); + } + + CGAL_assertion (amap2.is_valid()); + + return res; +} + +template<unsigned int dim, typename CMap1, typename CMap2> +std::size_t copy_cell(CMap1& amap1, typename CMap1::Dart_handle dh, + CMap2& amap2, + std::unordered_map + <typename CMap1::Dart_handle, typename CMap2::Dart_handle>* + origin_to_copy=nullptr, + std::unordered_map + <typename CMap1::Dart_handle, typename CMap2::Dart_handle>* + copy_to_origin=nullptr, + bool copy_perforated_darts=false, + typename CMap1::size_type mark_perforated=CMap1::INVALID_MARK) +{ + std::tuple<> converters; + CGAL::Default_converter_dart_info<typename CMap1::Refs, + typename CMap2::Refs> dartinfoconverter; + CGAL::Default_converter_cmap_0attributes_with_point<typename CMap1::Refs, + typename CMap2::Refs> pointconverter; + return copy_cell<dim>(amap1, dh, amap2, origin_to_copy, copy_to_origin, + converters, dartinfoconverter, pointconverter, + copy_perforated_darts, mark_perforated); +} + +#endif // CMAP_COPY_H diff --git a/src/cmap_isomorphisms.h b/src/cmap_isomorphisms.h new file mode 100644 index 0000000000000000000000000000000000000000..163ed29fa54dd4ac9e96e9e36ec95f3724ad5e2f --- /dev/null +++ b/src/cmap_isomorphisms.h @@ -0,0 +1,435 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// + +#ifndef CMAP_ISOMORPHISMS_H +#define CMAP_ISOMORPHISMS_H + +#include <CGAL/Handle_hash_function.h> +#include <CGAL/Unique_hash_map.h> +#include <CGAL/Combinatorial_map/internal/Combinatorial_map_internal_functors.h> + +#include <queue> +#include <functional> + +/// @pre both faces are a cycle of darts (no 1-boundary). +template<typename LCC1, typename LCC2> +bool are_faces_isomorphic_from_darts(LCC1& lcc1, typename LCC1::Dart_handle sd1, + LCC2& lcc2, typename LCC2::Dart_handle sd2, + typename LCC1::size_type marktopreserve1, + typename LCC2::size_type marktopreserve2, + std::function<typename LCC1::Dart_handle + (typename LCC1::Dart_handle)> next1, + std::function<typename LCC2::Dart_handle + (typename LCC2::Dart_handle)> next2, + bool testDartInfo=true, + bool testAttributes=true, + bool testPoint=true) +{ + if(marktopreserve1!=LCC1::INVALID_MARK && + marktopreserve2!=LCC2::INVALID_MARK && + lcc1.is_marked(sd1, marktopreserve1)!=lcc2.is_marked(sd2, marktopreserve2)) + { return false; } + + bool match=true; + typename LCC1::Dart_handle dh1=sd1; + typename LCC2::Dart_handle dh2=sd2; + do + { + if(marktopreserve1!=LCC1::INVALID_MARK && + marktopreserve2!=LCC2::INVALID_MARK && + lcc1.is_marked(dh1, marktopreserve1)!=lcc2.is_marked(dh2, marktopreserve2)) + { match=false; } + + // We first test info of darts + if(match && testDartInfo) + { match=CGAL::internal::template Test_is_same_dart_info_functor<LCC1, LCC2>:: + run(lcc1, lcc2, dh1, dh2); } + + // We need to test in both direction because + // Foreach_enabled_attributes only test non void attributes + // of Self. Functor Test_is_same_attribute_functor will modify + // the value of match to false if attributes do not match + if(testAttributes) + { + if (match) + { LCC1::Helper::template Foreach_enabled_attributes + <CGAL::internal::template Test_is_same_attribute_functor + <LCC1, LCC2>>::run(lcc1, lcc2, dh1, dh2, match); } + if (match) + { LCC2::Helper::template Foreach_enabled_attributes + <CGAL::internal::template Test_is_same_attribute_functor + <LCC2, LCC1>>::run(lcc2, lcc1, dh2, dh1, match); } + } + + if(match && testPoint) + { + // Only point of 0-attributes are tested. TODO test point of all + // attributes ? + match=CGAL::internal::template Test_is_same_attribute_point_functor + <LCC1, LCC2, 0>::run(lcc1, lcc2, dh1, dh2); + } + + if(match) + { + dh1=next1(dh1); + dh2=next2(dh2); + assert(dh1!=lcc1.null_dart_handle); + assert(dh2!=lcc2.null_dart_handle); + } + } + while(match && dh1!=sd1 && dh2!=sd2); + if(dh1!=sd1 || dh2!=sd2) { match=false; } + + return match; +} + +/// Test if face(sd) is isomorphic with the border of the fpattern +/// @pre both faces are a cycle of darts (no 1-boundary). +/// @return the dart of vpattern satisfying the bijection, nullprt if there is +/// no isomorphism. +template<typename LCC1, typename LCC2> +typename LCC2::Dart_handle is_face_isomorphic_to_fpattern +(LCC1& lcc, typename LCC1::Dart_handle sd, + LCC2& fpattern, + typename LCC1::size_type marktopreserve1=LCC1::INVALID_MARK, + typename LCC2::size_type marktopreserve2=LCC2::INVALID_MARK, + bool testDartInfo=true, + bool testAttributes=true, + bool testPoint=true) +{ + typename LCC2::Dart_handle res=nullptr; + for(auto it=fpattern.darts().begin(), itend=fpattern.darts().end(); + res==nullptr && it!=itend; ++it) + { + if(fpattern.template is_free<2>(it)) + { + if(are_faces_isomorphic_from_darts(lcc, sd, fpattern, it, + marktopreserve1, marktopreserve2, + + [&lcc](typename LCC1::Dart_handle dh) + -> typename LCC1::Dart_handle + { return lcc.template beta<1>(dh); }, + + [&fpattern](typename LCC2::Dart_handle dh) + -> typename LCC2::Dart_handle + { typename LCC2::Dart_handle other= + fpattern.template beta<1>(dh); + while(!fpattern.template is_free<2>(other)) + { other=fpattern.template beta<2,1>(other); } + return other; + }, + testDartInfo, + testAttributes, + testPoint)) + { res=it; } + } + } + return res; +} + +template<typename LCC1, typename LCC2> +bool are_volumes_isomorphic_from_darts(LCC1& lcc1, typename LCC1::Dart_handle sd1, + LCC2& lcc2, typename LCC2::Dart_handle sd2, + typename LCC1::size_type marktopreserve1, + typename LCC2::size_type marktopreserve2, + std::function<typename LCC1::Dart_handle + (typename LCC1::Dart_handle)> next1, + std::function<typename LCC1::Dart_handle + (typename LCC1::Dart_handle)> opposite1, + std::function<typename LCC2::Dart_handle + (typename LCC2::Dart_handle)> next2, + std::function<typename LCC2::Dart_handle + (typename LCC2::Dart_handle)> opposite2, + bool testDartInfo=true, + bool testAttributes=true, + bool testPoint=true) +{ + if(marktopreserve1!=LCC1::INVALID_MARK && + marktopreserve2!=LCC2::INVALID_MARK && + lcc1.is_marked(sd1, marktopreserve1)!=lcc2.is_marked(sd2, marktopreserve2)) + { return false; } + + bool match = true; + + // Two stacks used to run through the two maps. + std::deque<typename LCC1::Dart_handle> toTreat1; + std::deque<typename LCC2::Dart_handle> toTreat2; + + // A dart of this map is marked with m1 if its bijection was set + // (and similarly for mark m2 and darts of map2) + typename LCC1::size_type m1=lcc1.get_new_mark(); + typename LCC2::size_type m2=lcc2.get_new_mark(); + + // A dart of this map is marked with markpush if it was already pushed + // in the queue toTreat1. + typename LCC1::size_type markpush=lcc1.get_new_mark(); + + toTreat1.push_back(sd1); + toTreat2.push_back(sd2); + lcc1.mark(sd1, markpush); + + typename LCC1::Dart_handle dh1, other1; + typename LCC2::Dart_handle dh2, other2; + + CGAL::Unique_hash_map<typename LCC1::Dart_handle, + typename LCC2::Dart_handle, + typename CGAL::Handle_hash_function> bijection; + + while (match && !toTreat1.empty()) + { + // Next dart + dh1=toTreat1.front(); + toTreat1.pop_front(); + dh2=toTreat2.front(); + toTreat2.pop_front(); + + if(lcc1.is_marked(dh1, m1)!=lcc2.is_marked(dh2, m2)) + { match=false; } + else if(!lcc1.is_marked(dh1, m1)) + { + bijection[dh1]=dh2; + lcc1.mark(dh1, m1); + lcc2.mark(dh2, m2); + + // We first test info of darts + if(match && testDartInfo) + { match=CGAL::internal::template Test_is_same_dart_info_functor<LCC1, LCC2>:: + run(lcc1, lcc2, dh1, dh2); } + + // We need to test in both direction because + // Foreach_enabled_attributes only test non void attributes + // of Self. Functor Test_is_same_attribute_functor will modify + // the value of match to false if attributes do not match + if(testAttributes) + { + if (match) + { LCC1::Helper::template Foreach_enabled_attributes + <CGAL::internal::template Test_is_same_attribute_functor + <LCC1, LCC2>>::run(lcc1, lcc2, dh1, dh2, match); } + if (match) + { LCC2::Helper::template Foreach_enabled_attributes + <CGAL::internal::template Test_is_same_attribute_functor + <LCC2, LCC1>>::run(lcc2, lcc1, dh2, dh1, match); } + } + + if(match && testPoint) + { + // Only point of 0-attributes are tested. TODO test point of all + // attributes ? + match=CGAL::internal::template Test_is_same_attribute_point_functor + <LCC1, LCC2, 0>::run(lcc1, lcc2, dh1, dh2); + } + + if(match && + marktopreserve1!=LCC1::INVALID_MARK && + marktopreserve2!=LCC2::INVALID_MARK && + lcc1.is_marked(dh1, marktopreserve1)!= + lcc2.is_marked(dh2, marktopreserve2)) + { match=false; } + + // We test if the injection is valid with its neighboors. + // We go out as soon as it is not satisfied. + // Process next then opposite + other1=next1(dh1); other2=next2(dh2); + for(int i:{0,1}) + { + if(other1==lcc1.null_dart_handle && other2!=lcc2.null_dart_handle) + { match=false; } + else if(other1!=lcc1.null_dart_handle && other2==lcc2.null_dart_handle) + { match=false; } + else if(other1!=lcc1.null_dart_handle && other2!=lcc2.null_dart_handle) + { + if(lcc1.is_marked(other1, m1)!=lcc2.is_marked(other2, m2)) + { match=false; } + else + { + if(!lcc1.is_marked(other1, m1)) + { + if (!lcc1.is_marked(other1, markpush)) + { + toTreat1.push_back(other1); + toTreat2.push_back(other2); + lcc1.mark(other1, markpush); + } + } + else + { + if (bijection[other1]!=other2) + { match=false; } + } + } + } + if(i==0) { other1=opposite1(dh1); other2=opposite2(dh2); } + } + } + } + + // Here we test if both queue are empty + if(!toTreat1.empty() || !toTreat2.empty()) + { match=false; } + + // Here we unmark all the marked darts. + toTreat1.clear(); + toTreat2.clear(); + + toTreat1.push_back(sd1); + toTreat2.push_back(sd2); + lcc1.unmark(sd1, markpush); + + while (!toTreat1.empty()) + { + dh1=toTreat1.front(); + toTreat1.pop_front(); + dh2=toTreat2.front(); + toTreat2.pop_front(); + + lcc1.unmark(dh1, m1); + lcc2.unmark(dh2, m2); + + other1=next1(dh1); other2=next2(dh2); + for(int i:{0,1}) + { + if(other1!=lcc1.null_dart_handle && other2!=lcc2.null_dart_handle) + { + if (lcc1.is_marked(other1, markpush)) + { + toTreat1.push_back(other1); + toTreat2.push_back(other2); + lcc1.unmark(other1, markpush); + } + } + if(i==0) { other1=opposite1(dh1); other2=opposite2(dh2); } + } + } + + assert(lcc1.is_whole_map_unmarked(m1)); + assert(lcc1.is_whole_map_unmarked(markpush)); + assert(lcc2.is_whole_map_unmarked(m2)); + lcc1.free_mark(m1); + lcc1.free_mark(markpush); + lcc2.free_mark(m2); + + return match; +} + +/// Test if surface(sd) is isomorphic with the border of the spattern +/// @return the dart of spattern satisfying the bijection, nullprt if there is +/// no isomorphism. +template<typename LCC1, typename LCC2> +typename LCC2::Dart_handle is_surface_isomorphic_to_spattern +(LCC1& lcc, typename LCC1::Dart_handle sd, + LCC2& spattern, + typename LCC2::size_type faceborder, + typename LCC1::size_type marktopreserve1=LCC1::INVALID_MARK, + typename LCC2::size_type marktopreserve2=LCC2::INVALID_MARK, + bool testDartInfo=true, + bool testAttributes=true, + bool testPoint=true) +{ + typename LCC2::Dart_handle res=nullptr; + for(auto it=spattern.darts().begin(), itend=spattern.darts().end(); + res==nullptr && it!=itend; ++it) + { + if(spattern.is_marked(it, faceborder)) + { + if(are_volumes_isomorphic_from_darts(lcc, sd, spattern, it, + marktopreserve1, marktopreserve2, + + [&lcc](typename LCC1::Dart_handle dh) + -> typename LCC1::Dart_handle + { return lcc.template beta<1>(dh); }, + [&lcc](typename LCC1::Dart_handle dh) + -> typename LCC1::Dart_handle + { return lcc.template beta<2>(dh); }, + + [&spattern, faceborder](typename LCC2::Dart_handle dh) + -> typename LCC2::Dart_handle + { typename LCC2::Dart_handle other= + spattern.template beta<1>(dh); + while(!spattern.is_marked(other, faceborder)) + { other=spattern.template beta<2,1>(other); } + return other; + }, + + [&spattern](typename LCC2::Dart_handle dh) + -> typename LCC2::Dart_handle + { return spattern.template beta<2>(dh); }, + + testDartInfo, + testAttributes, + testPoint)) + { res=it; } + } + } + return res; +} + +/// Test if volume(sd) is isomorphic with the border of the vpattern +/// @return the dart of vpattern satisfying the bijection, nullprt if there is +/// no isomorphism. +template<typename LCC1, typename LCC2> +typename LCC2::Dart_handle is_volume_isomorphic_to_vpattern +(LCC1& lcc, typename LCC1::Dart_handle sd, + LCC2& vpattern, + typename LCC1::size_type marktopreserve1=LCC1::INVALID_MARK, + typename LCC2::size_type marktopreserve2=LCC2::INVALID_MARK, + bool testDartInfo=true, + bool testAttributes=true, + bool testPoint=true) +{ + typename LCC2::Dart_handle res=nullptr; + for(auto it=vpattern.darts().begin(), itend=vpattern.darts().end(); + res==nullptr && it!=itend; ++it) + { + if(vpattern.template is_free<3>(it)) + { + if(are_volumes_isomorphic_from_darts(lcc, sd, vpattern, it, + marktopreserve1, marktopreserve2, + + [&lcc](typename LCC1::Dart_handle dh) + -> typename LCC1::Dart_handle + { return lcc.template beta<1>(dh); }, + [&lcc](typename LCC1::Dart_handle dh) + -> typename LCC1::Dart_handle + { return lcc.template beta<2>(dh); }, + + [&vpattern](typename LCC2::Dart_handle dh) + -> typename LCC2::Dart_handle + { return vpattern.template beta<1>(dh); }, + [&vpattern](typename LCC2::Dart_handle dh) + -> typename LCC2::Dart_handle + { typename LCC2::Dart_handle other= + vpattern.template beta<2>(dh); + while(!vpattern.template is_free<3>(other)) + { other=vpattern.template beta<3,2>(other); } + return other; + }, + + testDartInfo, + testAttributes, + testPoint)) + { res=it; } + } + } + return res; +} + +#endif // CMAP_ISOMORPHISMS_H diff --git a/src/cmap_query_replace.h b/src/cmap_query_replace.h new file mode 100644 index 0000000000000000000000000000000000000000..25674a3f9b8ba688de1509ecd03ee26d7f050f6c --- /dev/null +++ b/src/cmap_query_replace.h @@ -0,0 +1,1025 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef CMAP_QUERY_REPLACE_H +#define CMAP_QUERY_REPLACE_H + +#include <filesystem> +#include <limits> +#include <queue> +#include <sstream> +#include <unordered_map> +#include <utility> +#include <vector> + +#include "cmap_copy.h" // TEMPO TO REMOVE +#include <CGAL/draw_linear_cell_complex.h> // TEMPO TO REMOVE +#include "cmap_3close_cc.h" +#include "cmap_copy.h" +#include "cmap_query_replace_geometry.h" +#include "cmap_isomorphisms.h" +#include "cmap_signature.h" +#include "lcc_read_depending_extension.h" + +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +class Pattern_substituer +{ +public: + using Dart_handle=typename LCC::Dart_handle; + using size_type=typename LCC::size_type; + + using Signature_mapping=std::unordered_map<Signature, + std::pair<Dart_handle, std::size_t>>; + + template<unsigned int type> // type==1 for face, 2 for surface and 3 for volume + using Pattern_set=std::vector<Pattern<LCC, type>>; + + std::size_t number_of_fpatterns() const + { return m_fpatterns.size(); } + std::size_t number_of_spatterns() const + { return m_spatterns.size(); } + std::size_t number_of_vpatterns() const + { return m_vpatterns.size(); } + + LCC& fpattern(std::size_t i) + { return m_fpatterns[i].lcc(); } + LCC& spattern(std::size_t i) + { return m_spatterns[i].lcc(); } + LCC& vpattern(std::size_t i) + { return m_vpatterns[i].lcc(); } + + Signature_mapping& fsignatures() + { return m_fsignatures; } + Signature_mapping& ssignatures() + { return m_ssignatures; } + Signature_mapping& vsignatures() + { return m_vsignatures; } + + typename Signature_mapping::const_iterator find_fpattern(const Signature& s) const + { return m_fsignatures.find(s); } + typename Signature_mapping::const_iterator find_spattern(const Signature& s) const + { return m_ssignatures.find(s); } + typename Signature_mapping::const_iterator find_vpattern(const Signature& s) const + { return m_vsignatures.find(s); } + + typename Signature_mapping::const_iterator fpattern_end() const + { return m_fsignatures.end(); } + typename Signature_mapping::const_iterator spattern_end() const + { return m_ssignatures.end(); } + typename Signature_mapping::const_iterator vpattern_end() const + { return m_vsignatures.end(); } + + void load_fpatterns(const std::string& directory_name, + std::function<void(LCC&, size_type)> init_topreserve=nullptr) + { + load_all_patterns<1>(directory_name, m_fpatterns); + Signature signature; + Dart_handle dh; + size_type mark_to_preserve=LCC::INVALID_MARK; + std::size_t nb=0; + m_fsignatures.clear(); + for(auto& pattern: m_fpatterns) + { + if(init_topreserve!=nullptr) // true iff the std::function is not empty + { + mark_to_preserve=pattern.reserve_mark_to_preserve(); + init_topreserve(pattern.lcc(), mark_to_preserve); + } + dh=fsignature_of_pattern(pattern.lcc(), mark_to_preserve, signature, false); + auto res=m_fsignatures.find(signature); + if(res==m_fsignatures.end()) + { + pattern.compute_barycentric_coord(); + m_fsignatures[signature]=std::make_pair(dh, nb); + } + else + { + std::cout<<"[ERROR] load_fpatterns: two patterns have same signature " + <<nb<<" and "<<res->second.second<<std::endl; + } + ++nb; + // std::cout<<"[Pattern] Signature "<<nb<<": "; print_signature(signature); + } + } + void load_spatterns(const std::string& directory_name, + std::function<void(LCC&, size_type)> init_faceborder, + std::function<void(LCC&, size_type)> init_topreserve=nullptr) + { + load_all_patterns<2>(directory_name, m_spatterns); + Signature signature; + Dart_handle dh; + size_type mark_to_preserve=LCC::INVALID_MARK; + std::size_t nb=0; + m_ssignatures.clear(); + for(auto& pattern: m_spatterns) + { + init_faceborder(pattern.lcc(), pattern.m_mark_faceborder); + if(init_topreserve!=nullptr) // true iff the std::function is not empty + { + mark_to_preserve=pattern.reserve_mark_to_preserve(); + init_topreserve(pattern.lcc(), mark_to_preserve); + } + dh=ssignature_of_pattern(pattern.lcc(), pattern.m_mark_faceborder, + mark_to_preserve, signature, false); + auto res=m_ssignatures.find(signature); + if(res==m_ssignatures.end()) + { + pattern.compute_barycentric_coord(); + assert(pattern.lcc().is_marked(dh, pattern.m_mark_faceborder)); + m_ssignatures[signature]=std::make_pair(dh, nb); + } + else + { + std::cout<<"[ERROR] load_spatterns: two patterns have same signature " + <<nb<<" and "<<res->second.second<<std::endl; + } + ++nb; + // std::cout<<"[Pattern] Signature "<<nb<<": "; print_signature(signature); + } + } + void load_vpatterns(const std::string& directory_name, + std::function<void(LCC&, size_type)> init_topreserve=nullptr) + { + load_all_patterns<3>(directory_name, m_vpatterns); + Signature signature; + Dart_handle dh; + size_type mark_to_preserve=LCC::INVALID_MARK; + std::size_t nb=0; + m_vsignatures.clear(); + for(auto& pattern: m_vpatterns) + { + if(init_topreserve!=nullptr) // true iff the std::function is not empty + { + mark_to_preserve=pattern.reserve_mark_to_preserve(); + init_topreserve(pattern.lcc(), mark_to_preserve); + } + dh=vsignature_of_pattern(pattern.lcc(), mark_to_preserve, signature, false); + auto res=m_vsignatures.find(signature); + if(res==m_vsignatures.end()) + { + pattern.compute_barycentric_coord(); + m_vsignatures[signature]=std::make_pair(dh, nb); + } + else + { + std::cout<<"[ERROR] load_vpatterns: two patterns have same signature " + <<nb<<" and "<<res->second.second<<std::endl; + } + ++nb; + // std::cout<<"[Pattern] Signature "<<nb<<": "; print_signature(signature); + } + } + +protected: + template<unsigned int type> + void load_all_patterns(const std::string& directory_name, + Pattern_set<type>& patterns) + { + patterns.clear(); + std::size_t nb=0; + const std::filesystem::path dir(directory_name); + if(!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) + { return; } + + for(auto const& dir_entry: std::filesystem::directory_iterator{dir}) + { + if(dir_entry.is_regular_file() && + is_lcc_readable_file(dir_entry.path().string())) + { ++nb; } + } + + patterns.resize(nb); + nb=0; + // std::cout<<"##############################"<<std::endl; + for(auto const& dir_entry: std::filesystem::directory_iterator{dir}) + { + if(dir_entry.is_regular_file() && + is_lcc_readable_file(dir_entry.path().string())) + { + // std::cout<<"pattern "<<nb<<": "<<dir_entry.path().string()<<std::endl; + read_depending_extension(dir_entry.path().string(), + patterns[nb].lcc()); + ++nb; + } + } + } + + //////////////////////////////////////////////////////////////////////////////// + /// Compute the bijection between the external edges of the pattern and + /// the face isomorphic to this external boundary. + /// Mark the dart of the external faces of the pattern. + /// @input dh1 is a copy of a dart of the pattern into lcc + /// @input dh2 is a dart of the face into lcc + /// @pre the two elements must be isomorphic + void compute_face_bijection_from_pattern_to_dart(LCC& lcc, + Dart_handle dh1, + Dart_handle dh2, + size_type markexternal, + Dart_mapping<LCC>& + pattern_to_face) + { + assert(lcc.template is_free<2>(dh1)); + pattern_to_face.clear(); + Dart_handle cur1=dh1; + Dart_handle cur2=dh2; + do + { + pattern_to_face[cur1]=cur2; + lcc.mark(cur1, markexternal); + cur1=lcc.template beta<1>(cur1); + while(!lcc.template is_free<2>(cur1)) + { cur1=lcc.template beta<2,1>(cur1); } + cur2=lcc.template beta<1>(cur2); + } + while(cur1!=dh1); + } + + //////////////////////////////////////////////////////////////////////////////// + /// Compute the bijection between the edges of the pattern and + /// the faces isomorphic to these external boundaries. + /// 2-unsew all darts marked face border. + /// @input dh1 is a dart of the pattern (WARNING: and NOT a copy in LCC contrary to similar methods for face and volume) + /// @input dh2 is a dart of the face + /// @pre the two elements must be isomorphic + void compute_surface_bijection_from_pattern_to_dart(LCC& lcc, + Pattern<LCC, 2>& pattern, + Dart_handle dh1, + Dart_handle dh2, + Dart_mapping<LCC>& + pattern_to_global, + Dart_mapping<LCC>& + pattern_to_surface) + { + assert(!pattern.lcc().template is_free<2>(dh1)); + assert(pattern.lcc().is_marked(dh1, pattern.m_mark_faceborder)); + std::queue<std::pair<Dart_handle, Dart_handle>> to_treat; + size_type treated=pattern.lcc().get_new_mark(); + Dart_handle other1, other2; + pattern_to_surface.clear(); + to_treat.push(std::make_pair(dh1, dh2)); + pattern.lcc().mark(dh1, treated); + while(!to_treat.empty()) + { + auto cur=to_treat.front(); + to_treat.pop(); + pattern_to_surface[pattern_to_global[cur.first]]=cur.second; + + // Process beta1 + other1=pattern.lcc().template beta<1>(cur.first); + while(!pattern.lcc().is_marked(other1, pattern.m_mark_faceborder)) + { other1=pattern.lcc().template beta<2,1>(other1); } + + other2=lcc.template beta<1>(cur.second); + assert(other1!=lcc.null_handle && other2!=lcc.null_handle); + if(!pattern.lcc().is_marked(other1, treated)) + { + to_treat.push(std::make_pair(other1, other2)); + pattern.lcc().mark(other1, treated); + } + + // Process beta2 + other1=pattern.lcc().template beta<2>(cur.first); + other2=lcc.template beta<2>(cur.second); + assert(other1!=lcc.null_handle && other2!=lcc.null_handle); + if(!pattern.lcc().is_marked(other1, treated)) + { + to_treat.push(std::make_pair(other1, other2)); + pattern.lcc().mark(other1, treated); + } + } + + for(auto it=pattern.lcc().darts().begin(), + itend=pattern.lcc().darts().end(); it!=itend; ++it) + { + if(pattern.lcc().is_marked(it, pattern.m_mark_faceborder)) + { + pattern.lcc().unmark(it, treated); + if(!lcc.template is_free<2>(pattern_to_global[it])) + { lcc.template unsew<2>(pattern_to_global[it]); } + } + assert(!pattern.lcc().is_marked(it, treated)); + } + pattern.lcc().free_mark(treated); + } + + //////////////////////////////////////////////////////////////////////////////// + /// Compute the bijection between the external faces of the pattern and + /// the faces of the volume isomorphic to these external boundaries. + /// Mark the dart of the external boundary of the pattern. + /// @input dh1 is a copy of a dart of the pattern into lcc + /// @input dh2 is a dart of the volume into lcc + /// @pre the two elements must be isomorphic + void compute_volume_bijection_from_pattern_to_dart(LCC& lcc, + Dart_handle dh1, + Dart_handle dh2, + size_type markexternal, + Dart_mapping<LCC>& + pattern_to_volume) + { + assert(lcc.template is_free<3>(dh1)); + std::queue<std::pair<Dart_handle, Dart_handle>> to_treat; + Dart_handle other1, other2; + pattern_to_volume.clear(); + to_treat.push(std::make_pair(dh1, dh2)); + lcc.mark(dh1, markexternal); + while(!to_treat.empty()) + { + auto cur=to_treat.front(); + to_treat.pop(); + assert(lcc.template is_free<3>(cur.first)); + pattern_to_volume[cur.first]=cur.second; + + // Process beta1 + other1=lcc.template beta<1>(cur.first); + other2=lcc.template beta<1>(cur.second); + assert(other1!=lcc.null_handle && other2!=lcc.null_handle); + if(!lcc.is_marked(other1, markexternal)) + { + to_treat.push(std::make_pair(other1, other2)); + lcc.mark(other1, markexternal); + } + + // Process beta2 + other1=lcc.template beta<2>(cur.first); + while(!lcc.template is_free<3>(other1)) + { other1=lcc.template beta<3,2>(other1); } + other2=lcc.template beta<2>(cur.second); + assert(other1!=lcc.null_handle && other2!=lcc.null_handle); + if(!lcc.is_marked(other1, markexternal)) + { + to_treat.push(std::make_pair(other1, other2)); + lcc.mark(other1, markexternal); + } + } + } + +public: + +//////////////////////////////////////////////////////////////////////////////// +/// Replace volume(dh1) by the vpattern, knowing that the surface of +/// vpattern is isomorphic with volume(dh1) starting from the pair of darts +/// (dh1, dh2). +/// @pre the surface of vpattern is isomorphic with volume(dh1) +void replace_one_volume_from_dart(LCC& lcc, + Dart_handle dh1, + Pattern<LCC, 3>& vpattern, + Dart_handle dh2) +{ + Dart_mapping<LCC> pattern_to_global; + Dart_mapping<LCC> links_from_pattern_to_volume; + auto amark=lcc.get_new_mark(); + // 1) Copy pattern into lcc. New darts are not be marked + lcc.copy(vpattern.lcc(), &pattern_to_global); + // 2) Compute old_3sew to store 3-links of darts in volume(dh2) + assert(vpattern.lcc().darts().owns(dh2)); + compute_volume_bijection_from_pattern_to_dart + (lcc, pattern_to_global[dh2], dh1, amark, + links_from_pattern_to_volume); + transform_geometry_of_vpattern(lcc, links_from_pattern_to_volume, + pattern_to_global, vpattern); + + // 3) Remove all the external faces of the copy of the pattern, and 2-sew + // the internal faces of the copy of the pattern with the boundary of + // the volume. + std::vector<std::pair<Dart_handle, Dart_handle>> tosew; + Dart_handle otherdh; + tosew.reserve(links_from_pattern_to_volume.size()); + for(auto curdh: links_from_pattern_to_volume) + { + otherdh=lcc.template beta<2>(curdh.first); + if(lcc.is_dart_used(otherdh) && !lcc.is_marked(otherdh, amark)) + { + lcc.template topo_unsew<2>(curdh.first); + if(!lcc.template is_free<2>(curdh.second)) + { lcc.template unsew<2>(curdh.second); } + tosew.push_back(std::make_pair(curdh.second, otherdh)); + } + } + for(auto curdh: tosew) + { lcc.template sew<2>(curdh.first, curdh.second); } + + for(auto curdh: links_from_pattern_to_volume) + { lcc.erase_dart(curdh.first); } + // assert(lcc.is_valid()); + assert(lcc.is_whole_map_unmarked(amark)); + lcc.free_mark(amark); +} +//////////////////////////////////////////////////////////////////////////////// +std::size_t query_replace_one_volume_from_dart(LCC& lcc, + Dart_handle dh, + size_type marktopreserve) +{ + std::size_t replaced=std::numeric_limits<std::size_t>::max(); + Signature word_signature; + Dart_handle + dh2=vsignature_of_volume_for_dart(lcc, dh, marktopreserve, word_signature); //, true); + + if (dh2==nullptr) { return replaced; } + + Signature signature; + vsignature_of_volume(lcc, dh, marktopreserve, signature); + if(signature!=word_signature) { return replaced; } + + //std::cout<<"Source: "; print_signature(signature); + auto res=m_vsignatures.find(signature); + if(res!=m_vsignatures.end()) + { + replace_one_volume_from_dart(lcc, dh2, m_vpatterns[res->second.second], + res->second.first); + replaced=res->second.second; + } + // else { std::cout<<"NOT found"<<std::endl; } + return replaced; +} +//////////////////////////////////////////////////////////////////////////////// +/// Query volume(dh) in the set of patterns, and if one pattern matches, +/// replace volume(dh). +/// @return the index of the replaced pattern, max(std::size_t) if no match. +std::size_t query_replace_one_volume(LCC& lcc, + Dart_handle dh, + size_type marktopreserve=LCC::INVALID_MARK) +{ + Signature signature; + Dart_handle + dh2=vsignature_of_volume(lcc, dh, marktopreserve, signature); //, true); + std::size_t replaced=std::numeric_limits<std::size_t>::max(); + auto res=m_vsignatures.find(signature); + if(res!=m_vsignatures.end()) + { + replace_one_volume_from_dart(lcc, dh2, m_vpatterns[res->second.second], + res->second.first); + replaced=res->second.second; + } + else { // std::cout<<"volume NOT found"<<std::endl; + /* static std::size_t nberrors=0; // TODO REMOVE (or add an option to enable/disable dynamically) + LCC lcc_error; + copy_cell<3>(lcc, dh, lcc_error); + // CGAL::draw(lcc_error); + // save_object_3D(std::string("error"+std::to_string(nberrors++)+".mesh"), lcc_error); + CGAL::write_off(lcc_error, std::string("error"+(std::to_string(nberrors++)+ + ".off")).c_str()); */ + } + return replaced; +} +//////////////////////////////////////////////////////////////////////////////// +/// Query volume(dh) but without using signatures. If one pattern matches, +/// replace volume(dh). +/// @return the index of the replaced pattern, max(std::size_t) if no match. +std::size_t query_replace_one_volume_without_signature(LCC& lcc, + Dart_handle dh, + size_type marktopreserve=LCC::INVALID_MARK) +{ + Dart_handle res=nullptr, sd=dh; + + if(marktopreserve!=LCC::INVALID_MARK && !lcc.is_marked(dh, marktopreserve)) + { + auto it=lcc.template darts_of_cell<3>(dh).begin(), + itend=lcc.template darts_of_cell<3>(dh).end(); + while(it!=itend && !lcc.is_marked(it, marktopreserve)) + { ++it; } + if(it!=itend) { sd=it; } + } + + std::size_t i=0; + while(res==nullptr && i<number_of_vpatterns()) + { + res=is_volume_isomorphic_to_vpattern(lcc, sd, vpattern(i), marktopreserve, + m_vpatterns[i].mark_to_preserve(), + false, false, false); + if(res==nullptr) { ++i; } + } + + if(res!=nullptr) + { replace_one_volume_from_dart(lcc, sd, m_vpatterns[i], res); } + else + { i=std::numeric_limits<std::size_t>::max(); } + // std::cout<<"NOT found"<<std::endl; + return i; +} +//////////////////////////////////////////////////////////////////////////////// +/// Replace face(dh1) by the fpattern, knowing that the border of +/// fpattern is isomorphic with face(dh1) starting from the pair of darts +/// (dh1, dh2). +/// @pre the border of fpattern is isomorphic with face(dh1) +void replace_one_face_from_dart(LCC& lcc, + Dart_handle dh1, + Pattern<LCC, 1>& fpattern, + Dart_handle dh2) +{ + Dart_mapping<LCC> pattern_to_global; + Dart_mapping<LCC> links_from_pattern_to_face; + bool with_beta3=false; + // 1) Copy pattern into lcc. + lcc.copy(fpattern.lcc(), &pattern_to_global); + if(!lcc.template is_free<3>(dh1)) + { + close_cc_for_beta3(lcc, pattern_to_global[dh2]); + with_beta3=true; + } + // 2) Compute mapping from the boundary of the pattern and + // the face isomorphic to this external boundary + auto amark=lcc.get_new_mark(); + compute_face_bijection_from_pattern_to_dart + (lcc, pattern_to_global[dh2], dh1, amark, links_from_pattern_to_face); + transform_geometry_of_fpattern(lcc, links_from_pattern_to_face, + pattern_to_global, fpattern); + + // 3) Remove all the external edges of the copy of the pattern, and 1-sew + // the internal edges of the copy of the pattern with the boundary of + // the face. + std::vector<std::pair<Dart_handle, Dart_handle>> tosew0, tosew1; + tosew0.reserve(links_from_pattern_to_face.size()); + tosew1.reserve(links_from_pattern_to_face.size()); + Dart_handle otherdh; + for(auto curdh: links_from_pattern_to_face) + { + otherdh=lcc.template beta<0>(curdh.first); + if(lcc.is_dart_used(otherdh) && !lcc.is_marked(otherdh, amark)) + { + lcc.template topo_unsew<0>(curdh.first); + //lcc.template unsew<0>(curdh.first); + if(!lcc.template is_free<0>(curdh.second)) + { lcc.template unsew<0>(curdh.second); } + //lcc.template sew<0>(curdh.second, otherdh); + tosew0.push_back(std::make_pair(curdh.second, otherdh)); + } + otherdh=lcc.template beta<1>(curdh.first); + if(lcc.is_dart_used(otherdh) && !lcc.is_marked(otherdh, amark)) + { + lcc.template topo_unsew<1>(curdh.first); + //lcc.template unsew<1>(curdh.first); + if(!lcc.template is_free<1>(curdh.second)) + { lcc.template unsew<1>(curdh.second); } + //lcc.template sew<1>(curdh.second, otherdh); + tosew1.push_back(std::make_pair(curdh.second, otherdh)); + } + } + for(auto curdh: tosew0) + { lcc.template sew<0>(curdh.first, curdh.second); } + for(auto curdh: tosew1) + { lcc.template sew<1>(curdh.first, curdh.second); } + for(auto curdh: links_from_pattern_to_face) + { + if(with_beta3) + { lcc.erase_dart(lcc.template beta<3>(curdh.first)); } + lcc.erase_dart(curdh.first); + } + assert(lcc.is_whole_map_unmarked(amark)); + lcc.free_mark(amark); + // assert(lcc.is_valid()); +} +//////////////////////////////////////////////////////////////////////////////// +/// Query face(dh) in the set of patterns, and if one pattern matches, +/// replace face(dh). +/// @return the index of the replaced pattern, max(std::size_t) if no match. +std::size_t query_replace_one_face(LCC& lcc, + Dart_handle dh, + size_type marktopreserve=LCC::INVALID_MARK) +{ + Signature signature; + Dart_handle + dh2=fsignature_of_face(lcc, dh, marktopreserve, signature); //, true); + //std::cout<<"Source: "; print_signature(signature); + std::size_t replaced=std::numeric_limits<std::size_t>::max(); + auto res=m_fsignatures.find(signature); + if(res!=m_fsignatures.end()) + { + // std::cout<<"FOUND Pattern "<<res->second.second+1<<std::endl; + replace_one_face_from_dart(lcc, dh2, m_fpatterns[res->second.second], + res->second.first); + replaced=res->second.second; + } + // else { std::cout<<"face NOT found"<<std::endl; } + return replaced; +} +//////////////////////////////////////////////////////////////////////////////// +/// Query face(dh) but without using signatures. If one pattern matches, +/// replace face(dh). +/// @return the index of the replaced pattern, max(std::size_t) if no match. +std::size_t query_replace_one_face_without_signature(LCC& lcc, + Dart_handle dh, + size_type marktopreserve=LCC::INVALID_MARK) +{ + Dart_handle res=nullptr, sd=dh; + + if(marktopreserve!=LCC::INVALID_MARK && !lcc.is_marked(dh, marktopreserve)) + { + auto it=lcc.template darts_of_cell<2,2>(dh).begin(), + itend=lcc.template darts_of_cell<2,2>(dh).end(); + while(it!=itend && !lcc.is_marked(it, marktopreserve)) + { ++it; } + if(it!=itend) { sd=it; } + } + + std::size_t i=0; + while(res==nullptr && i<number_of_fpatterns()) + { + res=is_face_isomorphic_to_fpattern(lcc, sd, fpattern(i), marktopreserve, + m_fpatterns[i].mark_to_preserve(), + false, false, false); + if(res==nullptr) { ++i; } + } + + if(res!=nullptr) + { replace_one_face_from_dart(lcc, sd, m_fpatterns[i], res); } + else + { i=std::numeric_limits<std::size_t>::max(); } + // std::cout<<"NOT found"<<std::endl; + return i; +} +//////////////////////////////////////////////////////////////////////////////// +/// Replace surface(dh1) by the spattern, knowing that the border of +/// spattern is isomorphic with surface(dh1) starting from the pair of darts +/// (dh1, dh2). +/// @pre the border of spattern is isomorphic with surface(dh1) +void replace_one_surface_from_dart(LCC& lcc, + Dart_handle dh1, + Pattern<LCC, 2>& spattern, + Dart_handle dh2) +{ + Dart_mapping<LCC> pattern_to_global; + Dart_mapping<LCC> links_from_pattern_to_surface; + // 1) Copy pattern into lcc. + lcc.copy(spattern.lcc(), &pattern_to_global); + + // 2) Compute mapping from the boundary of the pattern and + // the surface isomorphic to this external boundary, and 2-unsew + // each face border of the pattern + auto amark=lcc.get_new_mark(); + compute_surface_bijection_from_pattern_to_dart(lcc, spattern, + dh2, dh1, + pattern_to_global, + links_from_pattern_to_surface); + + // Transform the geometry of all faces (same method than for faces) + transform_geometry_of_spattern(lcc, links_from_pattern_to_surface, + pattern_to_global, spattern); + + // 3) Remove all the external edges of the copy of the pattern, and 1-sew + // the internal edges of the copy of the pattern with the boundary of + // the face. + for(auto curdh: links_from_pattern_to_surface) + { + if(!lcc.template is_free<3>(curdh.second) && + lcc.template is_free<3>(curdh.first)) + { close_cc_for_beta3(lcc, curdh.first); } + + dh2=lcc.template beta<0>(curdh.first); + if(lcc.is_dart_used(dh2) && !lcc.is_marked(dh2, amark)) + { + lcc.template unsew<0>(curdh.first); + if(!lcc.template is_free<0>(curdh.second)) + { lcc.template unsew<0>(curdh.second); } + lcc.template sew<0>(curdh.second, dh2); + } + dh2=lcc.template beta<1>(curdh.first); + if(lcc.is_dart_used(dh2) && !lcc.is_marked(dh2, amark)) + { + lcc.template unsew<1>(curdh.first); + if(!lcc.template is_free<1>(curdh.second)) + { lcc.template unsew<1>(curdh.second); } + lcc.template sew<1>(curdh.second, dh2); + } + } + for(auto curdh: links_from_pattern_to_surface) + { + if(!lcc.template is_free<3>(curdh.first)) + { lcc.erase_dart(lcc.template beta<3>(curdh.first)); } + lcc.erase_dart(curdh.first); + } + assert(lcc.is_whole_map_unmarked(amark)); + lcc.free_mark(amark); +} +//////////////////////////////////////////////////////////////////////////////// +/// Query surface(dh) in the set of patterns, and if one pattern matches, +/// replace surface(dh). +/// @return the index of the replaced pattern, max(std::size_t) if no match. +std::size_t query_replace_one_surface(LCC& lcc, + Dart_handle dh, + size_type marktopreserve=LCC::INVALID_MARK) +{ + Signature signature; + Dart_handle + dh2=ssignature_of_surface(lcc, dh, marktopreserve, signature); //, true); + typename LCC::Vector v1, v2; + // std::cout<<"Source: "; print_signature(signature); + std::size_t replaced=std::numeric_limits<std::size_t>::max(); + auto res=m_ssignatures.find(signature); + if(res!=m_ssignatures.end()) + { + // std::cout<<"FOUND Pattern "<<res->second.second+1<<std::endl; + replace_one_surface_from_dart(lcc, dh2, m_spatterns[res->second.second], + res->second.first); + replaced=res->second.second; + // assert(lcc.is_valid()); + } + // else { std::cout<<"NOT found"<<std::endl; } + return replaced; +} +//////////////////////////////////////////////////////////////////////////////// +/// Query surface(dh) but without using signatures. If one pattern matches, +/// replace surface(dh). +/// @return the index of the replaced pattern, max(std::size_t) if no match. +std::size_t query_replace_one_surface_without_signature(LCC& lcc, + Dart_handle dh, + size_type marktopreserve=LCC::INVALID_MARK) +{ + Dart_handle res=nullptr, sd=dh; + + if(marktopreserve!=LCC::INVALID_MARK && !lcc.is_marked(dh, marktopreserve)) + { + auto it=lcc.template darts_of_cell<3>(dh).begin(), + itend=lcc.template darts_of_cell<3>(dh).end(); + while(it!=itend && !lcc.is_marked(it, marktopreserve)) + { ++it; } + if(it!=itend) { sd=it; } + } + + std::size_t i=0; + while(res==nullptr && i<number_of_spatterns()) + { + res=is_surface_isomorphic_to_spattern(lcc, sd, spattern(i), + m_spatterns[i].m_mark_faceborder, + marktopreserve, + m_spatterns[i].mark_to_preserve(), + false, false, false); + if(res==nullptr) { ++i; } + } + + if(res!=nullptr) + { replace_one_surface_from_dart(lcc, sd, m_spatterns[i], res); } + else + { i=std::numeric_limits<std::size_t>::max(); } + // std::cout<<"NOT found"<<std::endl; + return i; +} +//////////////////////////////////////////////////////////////////////////////// +std::size_t replace_vpatterns(LCC& lcc, + size_type marktopreserve, + bool nosignature=false, + bool all=true, + bool trace=false) +{ + auto amark=lcc.get_new_mark(); + lcc.negate_mark(amark); // All darts are marked + std::size_t res=0; + for(auto it=lcc.darts().begin(); it!=lcc.darts().end(); ++it) + { + if(lcc.is_marked(it, amark)) + { + lcc.template unmark_cell<3>(it, amark); + // New darts will not be marked + std::size_t replaced= + (nosignature?query_replace_one_volume_without_signature + (lcc, it, marktopreserve): + query_replace_one_volume(lcc, it, marktopreserve)); + if(replaced!=std::numeric_limits<std::size_t>::max()) + { + ++res; + if(!all) + { + lcc.free_mark(amark); + return true; + } + if(trace) { std::cout<<replaced+1<<" "; } + } + } + } + + lcc.free_mark(amark); + return res; +} +//////////////////////////////////////////////////////////////////////////////// +std::size_t replace_spatterns(LCC& lcc, + size_type marktopreserve, + bool nosignature=false, + bool all=true, + bool trace=false) +{ + auto amark=lcc.get_new_mark(); + lcc.negate_mark(amark); // All darts are marked + std::size_t res=0; + for(auto it=lcc.darts().begin(); it!=lcc.darts().end(); ++it) + { + if(lcc.is_marked(it, amark)) + { + lcc.template unmark_cell<3>(it, amark); + // New darts will not be marked + std::size_t replaced= + (nosignature?query_replace_one_surface_without_signature + (lcc, it, marktopreserve): + query_replace_one_surface(lcc, it, marktopreserve)); + if(replaced!=std::numeric_limits<std::size_t>::max()) + { + ++res; + if(!all) + { + lcc.free_mark(amark); + return true; + } + if(trace) { std::cout<<replaced+1<<" "; } + } + } + } + + lcc.free_mark(amark); + return res; +} +//////////////////////////////////////////////////////////////////////////////// +std::size_t replace_fpatterns(LCC& lcc, + size_type marktopreserve, + bool nosignature=false, + bool all=true, + bool trace=false) +{ + auto amark=lcc.get_new_mark(); + lcc.negate_mark(amark); // All darts are marked + std::size_t res=0; + for(auto it=lcc.darts().begin(); it!=lcc.darts().end(); ++it) + { + if(lcc.is_marked(it, amark)) + { + lcc.template unmark_cell<2>(it, amark); + // New darts will not be marked + std::size_t replaced= + (nosignature?query_replace_one_face_without_signature + (lcc, it, marktopreserve): + query_replace_one_face(lcc, it, marktopreserve)); + if(replaced!=std::numeric_limits<std::size_t>::max()) + { + ++res; + if(!all) + { + lcc.free_mark(amark); + return true; + } + if(trace) { std::cout<<replaced+1<<" "; } + } + } + } + + lcc.free_mark(amark); + return res; +} +//////////////////////////////////////////////////////////////////////////////// +void generate_all_face_replacement(LCC& lcc, + size_type marktopreserve, + std::list<LCC>& reslccs) +{ + std::list<LCC> totreat; // list to avoid copy of LCC + totreat.push_back(LCC()); + totreat.back()=lcc; // copy + while(!totreat.empty()) + { + LCC current=std::move(totreat.front()); + totreat.pop_front(); + + bool replaced=false; + for(auto it=current.darts().begin(), itend=current.darts().end(); + !replaced && it!=itend; ++it) + { + Signature signature; + fsignature_of_face_for_dart(current, it, marktopreserve, signature); //, true); + auto res=m_fsignatures.find(signature); + if(res!=m_fsignatures.end()) + { + Dart_handle dh2=it; + do + { + totreat.push_back(LCC()); + std::unordered_map<Dart_handle, Dart_handle> origin_to_copy; + totreat.back().copy(current, &origin_to_copy, nullptr); + replace_one_face_from_dart(totreat.back(), origin_to_copy[dh2], + m_fpatterns[res->second.second], + res->second.first); + replaced=true; + do + { + dh2=current.template beta<1>(dh2); + fsignature_of_face_for_dart(current, dh2, marktopreserve, signature); //, true); + res=m_fsignatures.find(signature); + } + while(res==m_fsignatures.end() && dh2!=it); + } + while(dh2!=it); + } + } + if(!replaced) + { + reslccs.push_back(LCC()); + current.swap(reslccs.back()); + } + } +} +//////////////////////////////////////////////////////////////////////////////// +void generate_all_surface_replacement(LCC& lcc, + size_type marktopreserve, + std::list<LCC>& reslccs) +{ + std::list<LCC> totreat; // list to avoid copy of LCC + totreat.push_back(LCC()); + totreat.back()=lcc; + while(!totreat.empty()) + { + LCC current; + current->swap(totreat.front()); + totreat.pop_front(); + + std::size_t replaced=std::numeric_limits<std::size_t>::max(); + for(auto it=current.darts().begin(), itend=current.darts().end(); + it!=itend; ++it) + { + Signature signature; + Dart_handle + dh2=ssignature_of_surface_for_dart(current, it, marktopreserve, signature); //, true); + auto res=m_ssignatures.find(signature); + if(res!=m_ssignatures.end()) + { + totreat.push_back(LCC()); + std::unordered_map<Dart_handle, Dart_handle> origin_to_copy; + totreat.back().copy(current, &origin_to_copy, nullptr); + replace_one_surface_from_dart(totreat.back(), origin_to_copy[dh2], + m_spatterns[res->second.second], + res->second.first); + replaced=res->second.second; + } + } + if(replaced==std::numeric_limits<std::size_t>::max()) + { + reslccs.push_back(LCC()); + current->swap(reslccs.back()); + } + } +} +//////////////////////////////////////////////////////////////////////////////// +void generate_all_volume_replacement(LCC& lcc, + size_type marktopreserve, + std::list<LCC>& reslccs) +{ + std::list<LCC> totreat; // list to avoid copy of LCC + totreat.push_back(LCC()); + totreat.back()=lcc; + while(!totreat.empty()) + { + LCC current; + current->swap(totreat.front()); + totreat.pop_front(); + + std::size_t replaced=std::numeric_limits<std::size_t>::max(); + for(auto it=current.darts().begin(), itend=current.darts().end(); + it!=itend; ++it) + { + Signature signature; + Dart_handle + dh2=vsignature_of_volume_for_dart(current, it, marktopreserve, signature); //, true); + auto res=m_vsignatures.find(signature); + if(res!=m_vsignatures.end()) + { + totreat.push_back(LCC()); + std::unordered_map<Dart_handle, Dart_handle> origin_to_copy; + totreat.back().copy(current, &origin_to_copy, nullptr); + replace_one_volume_from_dart(totreat.back(), origin_to_copy[dh2], + m_vpatterns[res->second.second], + res->second.first); + replaced=res->second.second; + } + } + if(replaced==std::numeric_limits<std::size_t>::max()) + { + reslccs.push_back(LCC()); + current->swap(reslccs.back()); + } + } +} + +public: + Pattern_set<1> m_fpatterns; + Signature_mapping m_fsignatures; + + Pattern_set<2> m_spatterns; + Signature_mapping m_ssignatures; + + Pattern_set<3> m_vpatterns; + Signature_mapping m_vsignatures; +}; +//////////////////////////////////////////////////////////////////////////////// +#endif // CMAP_QUERY_REPLACE_H diff --git a/src/cmap_query_replace_geometry.h b/src/cmap_query_replace_geometry.h new file mode 100644 index 0000000000000000000000000000000000000000..b807fa2ab12fc9e6c7fa988dcf182193e5807551 --- /dev/null +++ b/src/cmap_query_replace_geometry.h @@ -0,0 +1,893 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef CMAP_QUERY_REPLACE_GEOMETRY_H +#define CMAP_QUERY_REPLACE_GEOMETRY_H + +#include <unordered_map> +#include <utility> +#include <vector> +#include <tuple> +#include <queue> + +#include <CGAL/Kernel_traits.h> + +#include "cmap_3close_cc.h" +#include "cmap_signature.h" +#include "lcc_geometry_transformation.h" +#include "lcc_read_depending_extension.h" + +template<class LCC> +class Pattern_substituer; + +template<class LCC, unsigned int> +class Pattern; + +template<class LCC> +using Dart_mapping=std::unordered_map<typename LCC::Dart_handle, + typename LCC::Dart_handle>; + +/////////////////////////////////////////////////////////////////////////////// +template<class LCC, unsigned int type> +class Barycentric_coord +{}; + +template<class LCC> +class Barycentric_coord<LCC, 1> +{ +public: + using Dart_handle=typename LCC::Dart_handle; + + void display(LCC& lcc) + { + std::cout<<lcc.point(m_dart)<<": "; + for(auto& it: m_coords) + { std::cout<<"["<<" "<<lcc.point(std::get<0>(it))<<": "<<std::get<1>(it) + <<", "<<std::get<2>(it)<<", "<<std::get<3>(it)<<"] "; } + std::cout<<std::endl; + } + + Dart_handle m_dart; // dart of an inner vertex + /// barycentric coords of this inner vertex for each border vertex + std::vector<std::tuple<Dart_handle, double, double, double>> m_coords; +}; +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +class Barycentric_coord<LCC, 3> +{ +public: + using Dart_handle=typename LCC::Dart_handle; + + void display(LCC& lcc) + { + std::cout<<lcc.point(m_dart)<<": "; + for(auto& it: m_coords) + { std::cout<<"["<<" "<<lcc.point(std::get<0>(it))<<": "<<std::get<1>(it)<<", " + <<", "<<std::get<2>(it)<<", "<<std::get<3>(it)<<", " + <<std::get<4>(it)<<"] "; } + std::cout<<std::endl; + } + + Dart_handle m_dart; + std::vector<std::tuple<Dart_handle, double, double, double, double>> m_coords; +}; +/////////////////////////////////////////////////////////////////////////////// +template<typename Point> +bool compute_alpha_beta_gamma_of_point(const Point& a, const Point& b, + const Point& c, const Point& p, + double& alpha, double& beta, double& gamma) +{ + typename CGAL::Kernel_traits<Point>::Kernel::Triangle_3 t(a, b, c); + if(t.is_degenerate()) { return false; } + + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vap(a, p); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vbp(b, p); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vcp(c, p); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vab(a, b); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vac(a, c); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vca(c, a); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vbc(b, c); + + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 + n=CGAL::cross_product(vab, vac); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 + na=CGAL::cross_product(vbc, vbp); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 + nb=CGAL::cross_product(vca, vcp); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 + nc=CGAL::cross_product(vab, vap); + + alpha=(n*na)/(n*n); + beta=(n*nb)/(n*n); + gamma=(n*nc)/(n*n); + return true; + + /* typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 v0(p0, p1); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 v1(p0, p2); + + double div=v0.y()*v1.z()-v1.y()*v0.z(); + if(div!=0) + { beta=(v0.y()*(p.z()-p0.z())-(p.y()-p0.y())*v0.z())/(div); } + else + { + div=v0.x()*v1.z()-v1.x()*v0.z(); + if(div!=0) + { beta=(v0.x()*(p.z()-p0.z())-(p.x()-p0.x())*v0.z())/(div); } + else + { + div=v0.y()*v1.x()-v1.y()*v0.x(); + assert(div!=0); + beta=(v0.y()*(p.x()-p0.x())-(p.y()-p0.y())*v0.x())/(div); + } + } + + if(v0.x()!=0) + { alpha=((p.x()-p0.x())-beta*v1.x())/v0.x(); } + else if (v0.y()!=0) + { alpha=((p.y()-p0.y())-beta*v1.y())/v0.y(); } + else + { + assert(v1.z()!=0); + alpha=((p.z()-p0.z())-beta*v1.z())/v0.z(); + }*/ +} +/////////////////////////////////////////////////////////////////////////////// +template<typename Point> +bool compute_point_from_alpha_beta_gamma(const Point& p0, const Point& p1, + const Point& p2, double alpha, + double beta, double gamma, + Point& p) +{ + typename CGAL::Kernel_traits<Point>::Kernel::Triangle_3 t(p0, p1, p2); + if(t.is_degenerate()) { return false; } + p=Point(alpha*p0.x()+beta*p1.x()+gamma*p2.x(), + alpha*p0.y()+beta*p1.y()+gamma*p2.y(), + alpha*p0.z()+beta*p1.z()+gamma*p2.z()); + return true; + + /* typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 v0(p0, p1); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 v1(p0, p2); + p=Point(p0.x()+alpha*v0.x()+beta*v1.x(), + p0.y()+alpha*v0.y()+beta*v1.y(), + p0.z()+alpha*v0.z()+beta*v1.z()); */ +} +/////////////////////////////////////////////////////////////////////////////// +template<typename Point> +bool compute_alpha_beta_gamma_delta_of_point(const Point& a, const Point& b, + const Point& c, const Point& d, + const Point& p, double& alpha, + double& beta, double& gamma, + double& delta) +{ + typename CGAL::Kernel_traits<Point>::Kernel::Tetrahedron_3 t(a, b, c, d); + if(t.is_degenerate()) { return false; } + + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vap(a, p); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vbp(b, p); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vcp(c, p); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vdp(d, p); + + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vab(a, b); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vac(a, c); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vad(a, d); + + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vbc(b, c); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 vbd(b, d); + + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 + temp=CGAL::cross_product(vac, vad); + + double va=(vbp*CGAL::cross_product(vbd, vbc)); + double vb=(vap*temp); + double vc=(vap*CGAL::cross_product(vad, vab)); + double vd=(vap*CGAL::cross_product(vab, vac)); + double v=/* std::abs */((vab*temp)); + + alpha=va/v; + beta=vb/v; + gamma=vc/v; + delta=vd/v; + return true; + + /* std::cout<<"[compute alpha...] "<<a<<" "<<b<<" "<<c<<" "<<d<<" " + <<alpha<<" "<<beta<<" "<<gamma<<" "<<delta<<" -> "<<p<<std::endl; */ + + /* + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 u(p0, p1); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 v(p0, p2); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 w(p0, p3); + + if((v.x()*u.y()-v.y()*u.x())!=0) + { + double part1=((p.x()*u.y()-p0.x()*u.y()-u.x()*p.y()+p0.y()*u.x())* + (v.z()*u.x()-v.x()*u.z()))/(v.x()*u.y()-v.y()*u.x()); + double part2=((w.y()*u.x()-w.x()*u.y())*(v.z()*u.x()-v.x()*u.z())+ + (v.x()*u.y()-v.y()*u.x())*(w.z()*u.x()-w.x()*u.z()))/ + (v.x()*u.y()-v.y()*u.x()); + assert(part2!=0); + gamma=((p.z()*u.x()-p0.z()*u.x()-p.x()*u.z()+p0.x()*u.z())-part1)/part2; + } + else if((v.x()*u.z()-v.z()*u.x())!=0) + { + double part1=((p.x()*u.z()-p0.x()*u.z()-u.x()*p.z()+p0.z()*u.x())* + (v.y()*u.x()-v.x()*u.y()))/(v.x()*u.z()-v.z()*u.x()); + double part2=((w.z()*u.x()-w.x()*u.z())*(v.y()*u.x()-v.x()*u.y())+ + (v.x()*u.z()-v.z()*u.x())*(w.y()*u.x()-w.x()*u.y()))/ + (v.x()*u.z()-v.z()*u.x()); + assert(part2!=0); + gamma=((p.y()*u.x()-p0.y()*u.x()-p.x()*u.y()+p0.x()*u.y())-part1)/part2; + } + else + { + assert((v.y()*u.z()-v.z()*u.y())!=0); + double part1=((p.y()*u.z()-p0.y()*u.z()-u.y()*p.z()+p0.z()*u.y())* + (v.x()*u.y()-v.y()*u.x()))/(v.y()*u.z()-v.z()*u.y()); + double part2=((w.z()*u.y()-w.y()*u.z())*(v.x()*u.y()-v.y()*u.x())+ + (v.y()*u.z()-v.z()*u.y())*(w.x()*u.y()-w.y()*u.x()))/ + (v.y()*u.z()-v.z()*u.y()); + assert(part2!=0); + gamma=((p.x()*u.y()-p0.x()*u.y()-p.y()*u.x()+p0.y()*u.x())-part1)/part2; + } + + if((v.x()*u.y()-v.y()*u.x())!=0) + { + beta=((p.x()*u.y()-p0.x()*u.y()-u.x()*p.y()+p0.y()*u.x())/ + (v.x()*u.y()-v.y()*u.x()))+ + ((w.y()*u.x()-w.x()*u.y())/(v.x()*u.y()-v.y()*u.x()))*gamma; + } + else if((v.x()*u.z()-v.z()*u.x())!=0) + { + beta=((p.x()*u.z()-p0.x()*u.z()-u.x()*p.z()+p0.z()*u.x())/ + (v.x()*u.z()-v.z()*u.x()))+ + ((w.z()*u.x()-w.x()*u.z())/(v.x()*u.z()-v.z()*u.x()))*gamma; + } + else + { + assert((v.y()*u.z()-v.z()*u.y())!=0); + beta=((p.y()*u.z()-p0.y()*u.z()-u.y()*p.z()+p0.z()*u.y())/ + (v.y()*u.z()-v.z()*u.y()))+ + ((w.z()*u.y()-w.y()*u.z())/(v.y()*u.z()-v.z()*u.y()))*gamma; + } + + if(u.x()!=0) + { alpha=(p.x()-p0.x()-beta*v.x()-gamma*w.x())/u.x(); } + else if(u.y()!=0) + { alpha=(p.y()-p0.y()-beta*v.y()-gamma*w.y())/u.y(); } + else + { + assert(u.z()!=0); + alpha=(p.z()-p0.z()-beta*v.z()-gamma*w.z())/u.z(); + }*/ + + // TODO assertion true if we use epsilon comparison + // assert(p.x()==alpha*a.x()+beta*b.x()+gamma*c.x()+delta*d.x()); + // assert(p.y()==alpha*a.y()+beta*b.y()+gamma*c.y()+delta*d.y()); + // assert(p.z()==alpha*a.z()+beta*b.z()+gamma*c.z()+delta*d.z()); +} +/////////////////////////////////////////////////////////////////////////////// +template<typename Point> +bool compute_point_from_alpha_beta_gamma_delta(const Point& p0, const Point& p1, + const Point& p2, const Point& p3, + double alpha, double beta, + double gamma, double delta, + Point& p) +{ + /* typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 u(p0, p1); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 v(p0, p2); + typename CGAL::Kernel_traits<Point>::Kernel::Vector_3 w(p0, p3); + p=Point(p0.x()+alpha*u.x()+beta*v.x()+gamma*w.x(), + p0.y()+alpha*u.y()+beta*v.y()+gamma*w.y(), + p0.z()+alpha*u.z()+beta*v.z()+gamma*w.z()); */ + typename CGAL::Kernel_traits<Point>::Kernel::Tetrahedron_3 t(p0, p1, p2, p3); + if(t.is_degenerate()) { return false; } + p=Point(alpha*p0.x()+beta*p1.x()+gamma*p2.x()+delta*p3.x(), + alpha*p0.y()+beta*p1.y()+gamma*p2.y()+delta*p3.y(), + alpha*p0.z()+beta*p1.z()+gamma*p2.z()+delta*p3.z()); + return true; + /* std::cout<<"[compute point] "<<p0<<" "<<p1<<" "<<p2<<" "<<p3<<" " + <<alpha<<" "<<beta<<" "<<gamma<<" "<<delta<<" -> "<<p<<std::endl; */ +} +/////////////////////////////////////////////////////////////////////////////// +template<typename LCC> +typename LCC::Point +compute_point_2D(LCC& lcc, + Dart_mapping<LCC>& links_from_pattern_to_face, + Dart_mapping<LCC>& pattern_to_global, + Barycentric_coord<LCC, 1>& m_barycentric_coords) +{ + typename LCC::Point p; + typename LCC::Vector res=CGAL::NULL_VECTOR; + typename LCC::Dart_handle cur, dh1, dh2; + std::size_t nb=0; + typename LCC::Dart_handle firstdh= + links_from_pattern_to_face[pattern_to_global + [std::get<0>(m_barycentric_coords.m_coords.front())]]; + typename LCC::Point bary2=lcc.template barycenter<2>(firstdh); + for(std::tuple<typename LCC::Dart_handle, double, double, double>& e: + m_barycentric_coords.m_coords) + { + assert(pattern_to_global.find(std::get<0>(e))!=pattern_to_global.end()); + assert(links_from_pattern_to_face.find(pattern_to_global[std::get<0>(e)]) + !=links_from_pattern_to_face.end()); + cur=links_from_pattern_to_face[pattern_to_global[std::get<0>(e)]]; + dh1=lcc.other_extremity(cur); + if(compute_point_from_alpha_beta_gamma(lcc.point(cur), lcc.point(dh1), + bary2, std::get<1>(e), + std::get<2>(e), std::get<3>(e), p)) + { + res+=typename LCC::Vector(p.x(), p.y(), p.z()); + ++nb; + } + } + assert(nb>0); + return typename LCC::Point(res.x()/nb, res.y()/nb, res.z()/nb); +} +/////////////////////////////////////////////////////////////////////////////// +template<typename LCC> +typename LCC::Point +compute_point_3D(LCC& lcc, + Dart_mapping<LCC>& links_from_pattern_to_volume, + Dart_mapping<LCC>& pattern_to_global, + Barycentric_coord<LCC, 3>& m_barycentric_coords) +{ + typename LCC::Point p; + typename LCC::Vector res=CGAL::NULL_VECTOR; + typename LCC::Dart_handle cur, dh1, dh2, dh3; + std::size_t nb=0; + for(std::tuple<typename LCC::Dart_handle, double, double, double, double>& e: + m_barycentric_coords.m_coords) + { + assert(pattern_to_global.find(std::get<0>(e))!=pattern_to_global.end()); + assert(links_from_pattern_to_volume.find(pattern_to_global[std::get<0>(e)]) + !=links_from_pattern_to_volume.end()); + cur=links_from_pattern_to_volume[pattern_to_global[std::get<0>(e)]]; + dh1=lcc.template beta<0>(cur); + dh2=lcc.other_extremity(cur); + dh3=lcc.template beta<2,1,2>(cur); + if(compute_point_from_alpha_beta_gamma_delta(lcc.point(cur), lcc.point(dh1), + lcc.point(dh2), lcc.point(dh3), + std::get<1>(e), std::get<2>(e), + std::get<3>(e), std::get<4>(e), + p)) + { + res+=typename LCC::Vector(p.x(), p.y(), p.z()); + ++nb; + } + } + assert(nb>0); + return typename LCC::Point(res.x()/nb, res.y()/nb, res.z()/nb); +} +/////////////////////////////////////////////////////////////////////////////// +template<typename LCC> +typename LCC::Point +compute_point_3D_v2(LCC& lcc, + Dart_mapping<LCC>& links_from_pattern_to_volume, + Dart_mapping<LCC>& pattern_to_global, + Barycentric_coord<LCC, 3>& m_barycentric_coords) +{ + assert(!m_barycentric_coords.m_coords.empty()); + typename LCC::Point p; + typename LCC::Vector res=CGAL::NULL_VECTOR; + typename LCC::Dart_handle cur, dh1, dh2, dh3; + std::size_t nb=0; + typename LCC::Dart_handle firstdh= + links_from_pattern_to_volume[pattern_to_global + [std::get<0>(m_barycentric_coords.m_coords.front())]]; + typename LCC::Point bary3=lcc.template barycenter<3>(firstdh); + for(std::tuple<typename LCC::Dart_handle, double, double, double, double>& e: + m_barycentric_coords.m_coords) + { + assert(pattern_to_global.find(std::get<0>(e))!=pattern_to_global.end()); + assert(links_from_pattern_to_volume.find(pattern_to_global[std::get<0>(e)]) + !=links_from_pattern_to_volume.end()); + cur=links_from_pattern_to_volume[pattern_to_global[std::get<0>(e)]]; + dh1=lcc.other_extremity(cur); + + // TODO avoid to recompute barycenters several times (?) + if(compute_point_from_alpha_beta_gamma_delta(lcc.point(cur), lcc.point(dh1), + lcc.template barycenter<2>(cur), + bary3, + std::get<1>(e), std::get<2>(e), + std::get<3>(e), std::get<4>(e), + p)) + { + res+=typename LCC::Vector(p.x(), p.y(), p.z()); + ++nb; + } + } + assert(nb>0); + return typename LCC::Point(res.x()/nb, res.y()/nb, res.z()/nb); +} +/////////////////////////////////////////////////////////////////////////////// +/// Transform the geometry of the fpattern according to the geometry of the +/// target. +template<typename LCC> +void transform_geometry_of_fpattern(LCC& lcc, + Dart_mapping<LCC>& links_from_pattern_to_face, + Dart_mapping<LCC>& pattern_to_global, + Pattern<LCC, 1>& pattern) +{ + for(Barycentric_coord<LCC, 1>& inner: pattern.barycentric_coords()) + { + assert(pattern_to_global.find(inner.m_dart)!=pattern_to_global.end()); + typename LCC::Dart_handle res=pattern_to_global[inner.m_dart]; + // TODO avoid to recompute barycenters several times (?) + lcc.point(res)=compute_point_2D(lcc, + links_from_pattern_to_face, + pattern_to_global, + inner); + } +} +/////////////////////////////////////////////////////////////////////////////// +/// Transform the geometry of the spattern according to the geometry of the +/// target. For now same method than transform_geometry_of_fpattern +template<typename LCC> +void transform_geometry_of_spattern(LCC& lcc, + Dart_mapping<LCC>& links_from_pattern_to_face, + Dart_mapping<LCC>& pattern_to_global, + Pattern<LCC, 2>& pattern) +{ + for(Barycentric_coord<LCC, 1>& inner: pattern.barycentric_coords()) + { + assert(pattern_to_global.find(inner.m_dart)!=pattern_to_global.end()); + typename LCC::Dart_handle res=pattern_to_global[inner.m_dart]; + // TODO avoid to recompute barycenters several times (?) + lcc.point(res)=compute_point_2D(lcc, + links_from_pattern_to_face, + pattern_to_global, + inner); + } +} +//////////////////////////////////////////////////////////////////////////////// +/// Transform the geometry of the vpattern according to the geometry of the +/// target. Mark the dart of the external faces of the pattern. +/// For now simple solution that does not work for any pattern. TODO better? +template<typename LCC> +void transform_geometry_of_vpattern(LCC& lcc, + Dart_mapping<LCC>& links_from_pattern_to_volume, + Dart_mapping<LCC>& pattern_to_global, + Pattern<LCC, 3>& pattern) +{ + for(Barycentric_coord<LCC, 3>& inner: pattern.barycentric_coords()) + { + assert(pattern_to_global.find(inner.m_dart)!=pattern_to_global.end()); + typename LCC::Dart_handle res=pattern_to_global[inner.m_dart]; + // std::cout<<"[transform_geometry_of_vpattern] "<<lcc.point()<<" -> before " + // <<lcc.point()<<" and after "; + /* lcc.point(res)=compute_point_3D(lcc, + links_from_pattern_to_volume, + pattern_to_global, + inner); */ + lcc.point(res)=compute_point_3D_v2(lcc, + links_from_pattern_to_volume, + pattern_to_global, + inner); + // std::cout<<lcc.point()<<std::endl; + } +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC, unsigned int type> // type==1 for face, 2 for surface, 3 for volume +class Pattern; +//////////////////////////////////////////////////////////////////////////////// +template<class LCC> +class Pattern<LCC, 1> // Face pattern +{ + friend class Pattern_substituer<LCC>; + + using Dart_handle=typename LCC::Dart_handle; + using size_type=typename LCC::size_type; + using Point=typename LCC::Point; + using Vector=typename LCC::Vector; +public: + Pattern(): m_mark_to_preserve(LCC::INVALID_MARK) + {} + + LCC& lcc() + { return m_lcc; } + + size_type reserve_mark_to_preserve() + { + if(m_mark_to_preserve==LCC::INVALID_MARK) + { m_mark_to_preserve=m_lcc.get_new_mark(); } + return m_mark_to_preserve; + } + + size_type mark_to_preserve() const + { return m_mark_to_preserve; } + + std::vector<Barycentric_coord<LCC, 1>>& barycentric_coords() + { return m_barycentric_coords; } + + void compute_barycentric_coord() + { + auto mark_vertices=m_lcc.get_new_mark(); + for(auto it=m_lcc.darts().begin(), itend=m_lcc.darts().end(); it!=itend; ++it) + { + if(!m_lcc.is_marked(it, mark_vertices)) + { + if(m_lcc.template is_free<2>(it)) // not an inner vertex + { m_lcc.template mark_cell<0>(it, mark_vertices); } + } + } + + typename LCC::Point bary2=CGAL::ORIGIN; + std::vector<Dart_handle> boundary_darts; + std::vector<Dart_handle> inner_vertices; + std::size_t nb1=0; + auto vertex_treated=m_lcc.get_new_mark(); + for(auto it=m_lcc.darts().begin(), itend=m_lcc.darts().end(); it!=itend; ++it) + { + if(m_lcc.template is_free<2>(it)) + { boundary_darts.push_back(it); } + if(!m_lcc.is_marked(it, vertex_treated)) + { // TODO we can improve the traversal of cells (regroup, use basic it...) + m_lcc.template mark_cell<0>(it, vertex_treated); + if(!m_lcc.is_marked(it, mark_vertices)) + { // Here it is incident to an inner vertex + m_lcc.template mark_cell<0>(it, mark_vertices); + inner_vertices.push_back(it); + } + else // Here is is incident to a vertex of the boundary + { + const Point& p=m_lcc.point(it); + bary2=Point(bary2.x()+p.x(), bary2.y()+p.y(), bary2.z()+p.z()); + ++nb1; + } + } + } + m_lcc.free_mark(mark_vertices); + m_lcc.free_mark(vertex_treated); + assert(nb1>0); + bary2=Point(bary2.x()/nb1, bary2.y()/nb1, bary2.z()/nb1); + + nb1=0; + m_barycentric_coords.resize(inner_vertices.size()); + for(auto it: inner_vertices) + { + m_barycentric_coords[nb1].m_coords.reserve(boundary_darts.size()); + m_barycentric_coords[nb1].m_dart=it; + ++nb1; + } + + double alpha, beta, gamma; + for(auto& itd: boundary_darts) + { + const Point& p0=m_lcc.point(itd); + const Point& p1=m_lcc.point(m_lcc.other_extremity(itd)); + nb1=0; + for(auto& it: inner_vertices) + { + if(compute_alpha_beta_gamma_of_point(p0, p1, bary2, m_lcc.point(it), + alpha, beta, gamma)) + { + m_barycentric_coords[nb1].m_coords.push_back + (std::make_tuple(itd, alpha, beta, gamma)); + ++nb1; + } + } + } + } + + void display() + { + for(auto& it: m_barycentric_coords) + { it.display(m_lcc); } + } + +protected: + LCC m_lcc; + typename LCC::size_type m_mark_to_preserve; + /// For each inner point, its barycentric coordinates for each external point + std::vector<Barycentric_coord<LCC, 1>> m_barycentric_coords; +}; +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +class Pattern<LCC, 2> // Surfacic pattern +{ + friend class Pattern_substituer<LCC>; + + using Dart_handle=typename LCC::Dart_handle; + using size_type=typename LCC::size_type; + using Point=typename LCC::Point; + using Vector=typename LCC::Vector; +public: + Pattern(): + m_mark_faceborder(m_lcc.get_new_mark()), + m_mark_to_preserve(LCC::INVALID_MARK) + {} + + LCC& lcc() + { return m_lcc; } + + size_type reserve_mark_to_preserve() + { + if(m_mark_to_preserve==LCC::INVALID_MARK) + { m_mark_to_preserve=m_lcc.get_new_mark(); } + return m_mark_to_preserve; + } + + size_type mark_to_preserve() const + { return m_mark_to_preserve; } + + // same barycentric coords than for face => 1 + std::vector<Barycentric_coord<LCC, 1>>& barycentric_coords() + { return m_barycentric_coords; } + + void compute_barycentric_coord() + { + // Mark vertices that belong to a face border. + auto border_vertex=m_lcc.get_new_mark(); + for(auto it=m_lcc.darts().begin(), itend=m_lcc.darts().end(); it!=itend; ++it) + { + if(!m_lcc.is_marked(it, border_vertex)) + { + if(m_lcc.is_marked(it, m_mark_faceborder)) // not an inner vertex + { m_lcc.template mark_cell<0>(it, border_vertex); } + } + } + + typename LCC::Point bary2=CGAL::ORIGIN; + std::vector<Dart_handle> boundary_darts; + std::vector<Dart_handle> inner_vertices; + std::size_t nb1=0, new_index=0; + Dart_handle cur, other; + auto treated=m_lcc.get_new_mark(); + auto vertex_treated=m_lcc.get_new_mark(); + std::queue<Dart_handle> to_treat; + for(auto it=m_lcc.darts().begin(), itend=m_lcc.darts().end(); it!=itend; ++it) + { + if(!m_lcc.is_marked(it, treated)) + { + boundary_darts.clear(); + inner_vertices.clear(); + nb1=0; + bary2=CGAL::ORIGIN; + + // Here we iterate through the cc of faces inside a same cycle of edges + // marked by m_mark_faceborder + to_treat.push(it); + m_lcc.mark(it, treated); + while(!to_treat.empty()) + { + cur=to_treat.front(); + to_treat.pop(); + + if(m_lcc.is_marked(cur, m_mark_faceborder)) + { boundary_darts.push_back(cur); } + + if(!m_lcc.is_marked(cur, vertex_treated)) + { // TODO we can improve the traversal of cells (regroup, use basic it...) + m_lcc.template mark_cell<0>(cur, vertex_treated); + if(!m_lcc.is_marked(cur, border_vertex)) + { // Here it is incident to an inner vertex + inner_vertices.push_back(cur); + } + else // Here is is incident to a vertex of the boundary + { + const Point& p=m_lcc.point(cur); + bary2=Point(bary2.x()+p.x(), bary2.y()+p.y(), bary2.z()+p.z()); + ++nb1; + } + } + + other=m_lcc.template beta<1>(cur); + if(!m_lcc.is_marked(other, treated)) + { + to_treat.push(other); + m_lcc.mark(other, treated); + } + + if(!m_lcc.is_marked(cur, m_mark_faceborder)) + { + other=m_lcc.template beta<2>(cur); + if(!m_lcc.is_marked(other, treated)) + { + to_treat.push(other); + m_lcc.mark(other, treated); + } + } + } + + assert(nb1>0); + // Now compute the barycentric coordinates of inner vertices + if(inner_vertices.size()>0) + { + bary2=Point(bary2.x()/nb1, bary2.y()/nb1, bary2.z()/nb1); + + new_index=m_barycentric_coords.size(); + nb1=new_index; + m_barycentric_coords.resize(m_barycentric_coords.size()+ + inner_vertices.size()); + for(auto iti: inner_vertices) + { + m_barycentric_coords[nb1].m_coords.reserve(boundary_darts.size()); + m_barycentric_coords[nb1].m_dart=iti; + ++nb1; + if(m_lcc.is_marked(iti, vertex_treated)) + { m_lcc.template unmark_cell<0>(iti, vertex_treated);} + } + + double alpha, beta, gamma; + for(auto& itd: boundary_darts) + { + const Point& p0=m_lcc.point(itd); + const Point& p1=m_lcc.point(m_lcc.other_extremity(itd)); + nb1=new_index; + for(auto& iti: inner_vertices) + { + if(compute_alpha_beta_gamma_of_point(p0, p1, bary2, m_lcc.point(iti), + alpha, beta, gamma)) + { + m_barycentric_coords[nb1].m_coords.push_back + (std::make_tuple(itd, alpha, beta, gamma)); + ++nb1; + } + } + if(m_lcc.is_marked(itd, vertex_treated)) + { m_lcc.template unmark_cell<0>(itd, vertex_treated);} + } + } + else + { + for(auto& itd: boundary_darts) + { + if(m_lcc.is_marked(itd, vertex_treated)) + { m_lcc.template unmark_cell<0>(itd, vertex_treated);} + } + } + assert(m_lcc.is_whole_map_unmarked(vertex_treated)); + } + } + assert(m_lcc.is_whole_map_marked(treated)); + m_lcc.free_mark(treated); + m_lcc.free_mark(border_vertex); + m_lcc.free_mark(vertex_treated); + } + + void display() + { + for(auto& it: m_barycentric_coords) + { it.display(m_lcc); } + } + +protected: + LCC m_lcc; + typename LCC::size_type m_mark_faceborder; + typename LCC::size_type m_mark_to_preserve; + /// For each inner point, its barycentric coordinates for each external point + std::vector<Barycentric_coord<LCC, 1>> m_barycentric_coords; +}; +//////////////////////////////////////////////////////////////////////////////// +template<class LCC> +class Pattern<LCC, 3> // Volumic pattern +{ + friend class Pattern_substituer<LCC>; + + using Dart_handle=typename LCC::Dart_handle; + using size_type=typename LCC::size_type; + using Point=typename LCC::Point; + using Vector=typename LCC::Vector; +public: + Pattern(): m_mark_to_preserve(LCC::INVALID_MARK) + {} + + LCC& lcc() + { return m_lcc; } + + size_type reserve_mark_to_preserve() + { + if(m_mark_to_preserve==LCC::INVALID_MARK) + { m_mark_to_preserve=m_lcc.get_new_mark(); } + return m_mark_to_preserve; + } + + size_type mark_to_preserve() const + { return m_mark_to_preserve; } + + std::vector<Barycentric_coord<LCC, 3>>& barycentric_coords() + { return m_barycentric_coords; } + + void compute_barycentric_coord() + { + auto mark_vertices=m_lcc.get_new_mark(); + for(auto it=m_lcc.darts().begin(), itend=m_lcc.darts().end(); it!=itend; ++it) + { + if(!m_lcc.is_marked(it, mark_vertices)) + { + if(m_lcc.template is_free<3>(it)) // not an inner vertex + { m_lcc.template mark_cell<0>(it, mark_vertices); } + } + } + + typename LCC::Point bary3=CGAL::ORIGIN; + std::vector<Dart_handle> boundary_darts; + std::vector<Dart_handle> inner_vertices; + std::size_t nb1=0; + auto vertex_treated=m_lcc.get_new_mark(); + for(auto it=m_lcc.darts().begin(), itend=m_lcc.darts().end(); it!=itend; ++it) + { + if(m_lcc.template is_free<3>(it)) + { boundary_darts.push_back(it); } + if(!m_lcc.is_marked(it, vertex_treated)) + { // TODO we can improve the traversal of cells (regroup, use basic it...) + m_lcc.template mark_cell<0>(it, vertex_treated); + if(!m_lcc.is_marked(it, mark_vertices)) + { // Here it is incident to an inner vertex + m_lcc.template mark_cell<0>(it, mark_vertices); + inner_vertices.push_back(it); + } + else // Here is is incident to a vertex of the boundary + { + const Point& p=m_lcc.point(it); + bary3=Point(bary3.x()+p.x(), bary3.y()+p.y(), bary3.z()+p.z()); + ++nb1; + } + } + } + m_lcc.free_mark(mark_vertices); + m_lcc.free_mark(vertex_treated); + assert(nb1>0); + bary3=Point(bary3.x()/nb1, bary3.y()/nb1, bary3.z()/nb1); + + nb1=0; + m_barycentric_coords.resize(inner_vertices.size()); + for(auto it: inner_vertices) + { + m_barycentric_coords[nb1].m_coords.reserve(boundary_darts.size()); + m_barycentric_coords[nb1].m_dart=it; + ++nb1; + } + + double alpha, beta, gamma, delta; + for(auto& itd: boundary_darts) + { + const Point& p0=m_lcc.point(itd); + const Point& p1=m_lcc.point(m_lcc.other_extremity(itd)); + const Point p2=m_lcc.template barycenter<2>(itd); + nb1=0; + for(auto& it: inner_vertices) + { // TODO we can test if tetra (p0, p1, p2, bary3) before to enter in the loop + if(compute_alpha_beta_gamma_delta_of_point(p0, p1, p2, bary3, + m_lcc.point(it), + alpha, beta, gamma, delta)) + { + m_barycentric_coords[nb1].m_coords.push_back + (std::make_tuple(itd, alpha, beta, gamma, delta)); + ++nb1; + } + } + } + } + + void display() + { + for(auto& it: m_barycentric_coords) + { it.display(m_lcc); } + } + +protected: + LCC m_lcc; + size_type m_mark_to_preserve; + /// For each inner point, its barycentric coordinates for each external point + std::vector<Barycentric_coord<LCC, 3>> m_barycentric_coords; +}; +//////////////////////////////////////////////////////////////////////////////// +#endif // CMAP_QUERY_REPLACE_GEOMETRY_H diff --git a/src/cmap_signature.h b/src/cmap_signature.h new file mode 100644 index 0000000000000000000000000000000000000000..23d48e0b8dea26b5fd3a0e29fd59ba8bbd09e5d1 --- /dev/null +++ b/src/cmap_signature.h @@ -0,0 +1,647 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef CMAP_SIGNATURE_H +#define CMAP_SIGNATURE_H + +#include <cassert> +#include <unordered_map> +#include <queue> +#include <functional> +#include <iostream> +#include <boost/container_hash/hash.hpp> + +// We have 3 type of words: +// fword for faces; sword for surfaces; vword for volumes +// 3 types of patterns: +// fpattern: a connected set of faces +// spattern: a closed surface (a set of connected faces without boundary) +// vpattern: a connected sef of volumes +// and 3 types of signatures: +// fsignature; ssignature; vsignature +/////////////////////////////////////////////////////////////////////////////// +using MyInt=std::uint16_t; +using Signature=std::vector<MyInt>; +//////////////////////////////////////////////////////////////////////////////// +namespace std { +template<> +class hash<Signature> +{ +public: + size_t operator() (const Signature& s) const + { + std::size_t seed=0; + for(auto n: s) + { boost::hash_combine(seed, n); } + return seed; + } +}; +} +//////////////////////////////////////////////////////////////////////////////// +void print_signature(const Signature& s) +{ + bool first=true; + std::cout<<"["; + for(auto n: s) + { if(!first) { std::cout<<" "; } else { first=false; } std::cout<<(int)n; } + std::cout<<"] "<<std::hash<Signature>()(s)<<std::endl; +} +/////////////////////////////////////////////////////////////////////////////// +//// Signature for faces ////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/// Compute the face word of the given map starting from a dart. +/// Takes one function next as parameter, allowing to change +/// the object considered (face or border of the fpattern) +/// If signature is non empty, compare the current word with signature, and +/// stop as soon as the word becomes bigger than signature. +/// @return true iff the computed word is the new minimal one +template<class CMAP> +bool compute_fword_from_dart(CMAP& cmap, + typename CMAP::Dart_handle dh, + typename CMAP::size_type marktopreserve, + Signature& word, + const Signature& signature, + std::function<typename CMAP::Dart_handle + (typename CMAP::Dart_handle)> next, + bool trace=false) +{ + word.clear(); + if(marktopreserve!=CMAP::INVALID_MARK && !cmap.is_marked(dh, marktopreserve)) + { return false; } + + if(!signature.empty()) { word.reserve(signature.size()); } + MyInt nb=0; + typename CMAP::Dart_handle cur=dh; + bool same_prefix=true, bigger=false; + do + { + nb=0; + do + { + ++nb; + cur=next(cur); + } + while(cur!=dh && + (marktopreserve==CMAP::INVALID_MARK || + !cmap.is_marked(cur, marktopreserve))); + word.push_back(nb); + if(same_prefix && !signature.empty()) + { + if(word.back()!=signature[word.size()-1]) + { + same_prefix=false; + if(word.back()>signature[word.size()-1]) + { + bigger=true; + assert(word>signature); + } + } + } + } + while(!bigger && cur!=dh); + + if(trace) + { + bool first=true; + std::cout<<"["; + for(auto n: word) + { if(!first) { std::cout<<" "; } else { first=false; } std::cout<<(int)n; } + std::cout<<"] "<<std::endl; + } + + if(signature.empty() || (!bigger && !same_prefix)) + { + assert(signature.empty() || word<signature); + return true; // word<signature + } + assert(!signature.empty() && word>=signature); + return false; // word>=signature + +} +/////////////////////////////////////////////////////////////////////////////// +/// Compute the face signature of the given pattern. +/// @pre cmap is a fpattern, i.e. a connected set of faces. +/// @return the initial dart of the signature +template<class CMAP> +typename CMAP::Dart_handle fsignature_of_pattern(CMAP& cmap, + typename CMAP::size_type marktopreserve, + Signature& signature, + bool trace=false) +{ + signature.clear(); + typename CMAP::Dart_handle res=nullptr; + Signature current_word; + + for(auto it=cmap.darts().begin(), itend=cmap.darts().end(); it!=itend; ++it) + { + if(cmap.template is_free<2>(it)) + { + if(compute_fword_from_dart(cmap, it, marktopreserve, current_word, signature, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { typename CMAP::Dart_handle other= + cmap.template beta<1>(dh); + while(!cmap.template is_free<2>(other)) + { other=cmap.template beta<2,1>(other); } + return other; + }, + + trace)) + { + res=it; + std::swap(current_word, signature); + if(signature.size()==1) + { return res; } // No need to test all starting darts if we have only one value + } + } + } + return res; +} +/////////////////////////////////////////////////////////////////////////////// +/// Compute the face signature of the given face. +/// @return the initial dart of the signature +template<class CMAP> +typename CMAP::Dart_handle fsignature_of_face_for_dart +(CMAP& cmap, typename CMAP::Dart_handle dh, + typename CMAP::size_type marktopreserve, Signature& signature, bool trace=false) +{ + signature.clear(); + typename CMAP::Dart_handle res=nullptr; + Signature current_word; + + if(compute_fword_from_dart(cmap, dh, marktopreserve, current_word, signature, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<1>(dh); }, + + trace)) + { + res=dh; + std::swap(current_word, signature); + } + return res; +} +/////////////////////////////////////////////////////////////////////////////// +/// Compute the face signature of the given face. +/// @return the initial dart of the signature +template<class CMAP> +typename CMAP::Dart_handle fsignature_of_face(CMAP& cmap, + typename CMAP::Dart_handle dh, + typename CMAP::size_type marktopreserve, + Signature& signature, + bool trace=false) +{ + signature.clear(); + typename CMAP::Dart_handle res=nullptr; + Signature current_word; + + typename CMAP::Dart_handle cur=dh; + do + { + if(compute_fword_from_dart(cmap, cur, marktopreserve, current_word, signature, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<1>(dh); }, + + trace)) + { + res=cur; + std::swap(current_word, signature); + if(signature.size()==1) + { return res; } // No need to test all starting darts if we have only one value + } + cur=cmap.template beta<1>(cur); + } + while(cur!=dh); + return res; +} +/////////////////////////////////////////////////////////////////////////////// +//// Signature for volumes///////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/// Compute the volume word of the given map starting from a dart. +/// Takes two functions next and opposite as parameter, allowing to change +/// the object considered (volume, surface...) +/// If signature is non empty, compare the current word with signature, and +/// stop as soon as the word becomes bigger than signature. +/// @return true iff the computed word is the new minimal one +template<class CMAP> +bool compute_vword_from_dart(CMAP& cmap, + typename CMAP::Dart_handle dh, + typename CMAP::size_type marktopreserve, + Signature& word, + const Signature& signature, + std::function<typename CMAP::Dart_handle + (typename CMAP::Dart_handle)> next, + std::function<typename CMAP::Dart_handle + (typename CMAP::Dart_handle)> opposite, + bool trace=false) +{ + word.clear(); + if(marktopreserve!=CMAP::INVALID_MARK && !cmap.is_marked(dh, marktopreserve)) + { return false; } + + if(!signature.empty()) { word.reserve(signature.size()); } + typename CMAP::size_type amark=cmap.get_new_mark(); + std::unordered_map<typename CMAP::Dart_handle, MyInt> indices; + std::queue<typename CMAP::Dart_handle> to_treat; + std::vector<typename CMAP::Dart_handle> to_unmark; + typename CMAP::Dart_handle cur, other; + bool same_prefix=true, bigger=false; + MyInt nb=1; + + to_treat.push(dh); + cmap.mark(dh, amark); + to_unmark.push_back(dh); + indices[dh]=nb++; + while(!bigger && !to_treat.empty()) + { + cur=to_treat.front(); + to_treat.pop(); + + if(marktopreserve==CMAP::INVALID_MARK || cmap.is_marked(cur, marktopreserve)) + { + word.push_back(0); + if(same_prefix && !signature.empty()) + { + if(word.back()!=signature[word.size()-1]) + { + same_prefix=false; + if(word.back()>signature[word.size()-1]) + { + bigger=true; + assert(word>signature); + } + } + } + } + + // Process next then opposite + for(auto f: {next, opposite}) + { + other=f(cur); + assert(other!=cmap.null_handle); + if(!cmap.is_marked(other, amark)) + { + to_treat.push(other); + cmap.mark(other, amark); + to_unmark.push_back(other); + assert(nb!=std::numeric_limits<MyInt>::max()); + indices[other]=nb++; + } + assert(indices.count(other)==1); + word.push_back(indices[other]); + if(same_prefix && !signature.empty()) + { + if(word.back()!=signature[word.size()-1]) + { + same_prefix=false; + if(word.back()>signature[word.size()-1]) + { + bigger=true; + assert(word>signature); + } + } + } + } + } + + if(trace) + { + bool first=true; + std::cout<<"["; + for(auto n: word) + { if(!first) { std::cout<<" "; } else { first=false; } std::cout<<(int)n; } + std::cout<<"] "<<std::endl; + } + + for(auto dhtou: to_unmark) + { cmap.unmark(dhtou, amark); } + cmap.free_mark(amark); + if(signature.empty() || (!bigger && !same_prefix)) + { + assert(signature.empty() || word<signature); + return true; // word<signature + } + assert(!signature.empty() && word>=signature); + return false; // word>=signature +} +/////////////////////////////////////////////////////////////////////////////// +/// Compute the volume signature of the given pattern. +/// @pre cmap is a connected set of volumes. +/// @return the initial dart of the signature +template<class CMAP> +typename CMAP::Dart_handle vsignature_of_pattern(CMAP& cmap, + typename CMAP::size_type marktopreserve, + Signature& signature, + bool trace=false) +{ + signature.clear(); + typename CMAP::Dart_handle res=nullptr; + Signature current_word; + + for(auto it=cmap.darts().begin(), itend=cmap.darts().end(); it!=itend; ++it) + { + if(cmap.template is_free<3>(it)) + { + if(compute_vword_from_dart(cmap, it, marktopreserve, current_word, signature, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<1>(dh); }, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { typename CMAP::Dart_handle other= + cmap.template beta<2>(dh); + while(!cmap.template is_free<3>(other)) + { other=cmap.template beta<3,2>(other); } + return other; + }, + + trace)) + { + res=it; + std::swap(current_word, signature); + } + } + } + return res; +} +/////////////////////////////////////////////////////////////////////////////// +/// Compute the vsignature of one volume but only for the given dart. +/// Function used only in order to validate all the patterns. Use +/// vsignature_of_volume instead. +/// @return the initial dart of the signature +template<class CMAP> +typename CMAP::Dart_handle vsignature_of_volume_for_dart +(CMAP& cmap, typename CMAP::Dart_handle dh, + typename CMAP::size_type marktopreserve, Signature& signature, bool trace=false) +{ + signature.clear(); + typename CMAP::Dart_handle res=nullptr; + Signature current_word; + + if(compute_vword_from_dart(cmap, dh, marktopreserve, current_word, signature, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<1>(dh); }, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<2>(dh); }, + + trace)) + { + res=dh; + std::swap(current_word, signature); + } + return res; +} +/////////////////////////////////////////////////////////////////////////////// +/// Compute the vsignature of one volume. +/// @return the initial dart of the signature +template<class CMAP> +typename CMAP::Dart_handle vsignature_of_volume(CMAP& cmap, + typename CMAP::Dart_handle dh, + typename CMAP::size_type marktopreserve, + Signature& signature, + bool trace=false) +{ + signature.clear(); + typename CMAP::Dart_handle res=nullptr; + Signature current_word; + + for(auto it=cmap.template darts_of_cell<3>(dh).begin(), + itend=cmap.template darts_of_cell<3>(dh).end(); it!=itend; ++it) + { + if(compute_vword_from_dart(cmap, it, marktopreserve, current_word, signature, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<1>(dh); }, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<2>(dh); }, + + trace)) + { + res=it; + std::swap(current_word, signature); + } + } + return res; +} +/////////////////////////////////////////////////////////////////////////////// +/// Compute the number of automorphisms of the vpattern of the given CMap, +/// knowing its vsignature. +/// @pre cmap is a vpattern, i.e. a connected set of volumes. +template<class CMAP> +std::size_t number_of_automorphisms_of_vpattern(CMAP& cmap, + typename CMAP::size_type marktopreserve, + const Signature& signature, + bool trace=false) +{ + Signature current_word; + std::size_t nb=0; + + for(auto it=cmap.darts().begin(), itend=cmap.darts().end(); it!=itend; ++it) + { + if(cmap.template is_free<3>(it)) + { + if(!compute_vword_from_dart(cmap, it, marktopreserve, current_word, signature, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<1>(dh); }, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { typename CMAP::Dart_handle other= + cmap.template beta<2>(dh); + while(!cmap.template is_free<3>(other)) + { other=cmap.template beta<3,2>(other); } + return other; + }, + + trace)) + { + // std::cout<<&*it<<" "; print_signature(current_word); + if(current_word==signature) { ++nb; } + } + } + } + return nb; +} +/////////////////////////////////////////////////////////////////////////////// +/// Compute the number of automorphisms of the volume, knowing its signature. +template<class CMAP> +std::size_t number_of_automorphisms_of_volume +(CMAP& cmap, typename CMAP::Dart_handle dh, + const Signature& signature, typename CMAP::size_type marktopreserve, + bool trace=false) +{ + Signature current_word; + std::size_t nb=0; + + for(auto it=cmap.template darts_of_cell<3>(dh).begin(), + itend=cmap.template darts_of_cell<3>(dh).end(); it!=itend; ++it) + { + if(!compute_vword_from_dart(cmap, it, marktopreserve, current_word, signature, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<1>(dh); }, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<2>(dh); }, + + trace)) + { + if(current_word==signature) { ++nb; } + } + } + return nb; +} +/////////////////////////////////////////////////////////////////////////////// +//// Signature for surfaces//////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/// Note: compute_vword_from_dart is reused to compute sword, only changing +/// the next and opposite methods. +/////////////////////////////////////////////////////////////////////////////// +/// Compute the ssignature of the given pattern. +/// @pre cmap is a spattern, i.e. a closed connected set of faces. +/// @return the initial dart of the signature +/// @note Be careful: contrary to f and vsignature, a ssignature does not exist +/// without some darts marked as face borders. Indeed, without marked darts, +/// it is not possible to know which edges are border of faces and which +/// ones are not. Note that this mark is only used for the pattern, not +/// for the target. +template<class CMAP> +typename CMAP::Dart_handle ssignature_of_pattern(CMAP& cmap, + typename CMAP::size_type faceborder, + typename CMAP::size_type marktopreserve, + Signature& signature, + bool trace=false) +{ + signature.clear(); + typename CMAP::Dart_handle res=nullptr; + Signature current_word; + + for(auto it=cmap.darts().begin(), itend=cmap.darts().end(); it!=itend; ++it) + { + if(cmap.is_marked(it, faceborder) && + compute_vword_from_dart(cmap, it, marktopreserve, current_word, signature, + + [&cmap, faceborder](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { typename CMAP::Dart_handle other= + cmap.template beta<1>(dh); + while(!cmap.is_marked(other, faceborder)) + { other=cmap.template beta<2,1>(other); } + return other; + }, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<2>(dh); }, + + trace)) + { + res=it; + std::swap(current_word, signature); + if(signature.size()==1) + { return res; } // No need to test all starting darts if we have only one value + } + } + return res; +} +/////////////////////////////////////////////////////////////////////////////// +/// Compute the ssignature of one surface. +/// @return the initial dart of the signature +template<class CMAP> +typename CMAP::Dart_handle ssignature_of_surface_for_dart +(CMAP& cmap, typename CMAP::Dart_handle dh, + typename CMAP::size_type marktopreserve, Signature& signature, bool trace=false) +{ + signature.clear(); + typename CMAP::Dart_handle res=nullptr; + Signature current_word; + + if(compute_vword_from_dart(cmap, dh, marktopreserve, current_word, signature, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<1>(dh); }, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<2>(dh); }, + + trace)) + { + res=dh; + std::swap(current_word, signature); + } + return res; +} +/////////////////////////////////////////////////////////////////////////////// +/// Compute the ssignature of one surface. +/// @return the initial dart of the signature +template<class CMAP> +typename CMAP::Dart_handle ssignature_of_surface(CMAP& cmap, + typename CMAP::Dart_handle dh, + typename CMAP::size_type marktopreserve, + Signature& signature, + bool trace=false) +{ + signature.clear(); + typename CMAP::Dart_handle res=nullptr; + Signature current_word; + + for(auto it=cmap.template darts_of_cell<3>(dh).begin(), + itend=cmap.template darts_of_cell<3>(dh).end(); it!=itend; ++it) + { + if(compute_vword_from_dart(cmap, it, marktopreserve, current_word, signature, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<1>(dh); }, + + [&cmap](typename CMAP::Dart_handle dh) + -> typename CMAP::Dart_handle + { return cmap.template beta<2>(dh); }, + + trace)) + { + res=it; + std::swap(current_word, signature); + } + } + return res; +} +/////////////////////////////////////////////////////////////////////////////// +#endif // CMAP_SIGNATURE_H diff --git a/src/hexa-subdivision.cpp b/src/hexa-subdivision.cpp new file mode 100644 index 0000000000000000000000000000000000000000..749673154d7066147d5366aed4dd21c88e136f6b --- /dev/null +++ b/src/hexa-subdivision.cpp @@ -0,0 +1,1664 @@ +// Copyright (c) 2011 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// + +// Prog that uses the query/replace method to generate transitions +// for hexaedral meshes (based on the work of Claudio). +// It uses the 325 vpatterns in hexa-325-patterns +// and the 5 fpatterns in square-5-patterns + +#include <CGAL/Linear_cell_complex_for_combinatorial_map.h> +#include <CGAL/Linear_cell_complex_operations.h> +#include <CGAL/Exact_predicates_inexact_constructions_kernel.h> +#include <CGAL/draw_linear_cell_complex.h> +#include <CGAL/Polyhedron_3.h> +#include <CGAL/AABB_tree.h> +#include <CGAL/AABB_traits.h> +#include <CGAL/AABB_face_graph_triangle_primitive.h> +#include <CGAL/Side_of_triangle_mesh.h> +#include <CGAL/Polygon_mesh_processing/triangulate_faces.h> +#include <CGAL/Aff_transformation_3.h> +#include <CGAL/aff_transformation_tags.h> + +#include <map> +#include <sys/types.h> +#include <unistd.h> + +#include "cmap_copy.h" +#include "cmap_query_replace.h" +#include "Compute_stats.h" +#include "init_to_preserve_for_query_replace.h" +#include "lcc_read_depending_extension.h" +#include "lcc_save_load_mesh.h" +#include "Print_txt.h" + +//////////////////////////////////////////////////////////////////////////////// +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point; +typedef Kernel::Vector_3 Vector; +//////////////////////////////////////////////////////////////////////////////// +template<typename Refs> +struct My_vertex: public CGAL::Cell_attribute_with_point<Refs> +{ + using Base=CGAL::Cell_attribute_with_point<Refs>; + + My_vertex(): + m_outside(false), + m_new_vertex(false) + {} + + My_vertex(const Point& p): + Base(p), + m_outside(false), + m_new_vertex(false) + {} + + bool m_outside; // Use to compute marching cube + bool m_new_vertex; // Use to compute marching cube +}; +//////////////////////////////////////////////////////////////////////////////// +class Myitems +{ +public: + template < class Refs > + struct Dart_wrapper + { + typedef My_vertex<Refs> Vertex_attrib; + typedef CGAL::Cell_attribute<Refs, unsigned int> Volume_attrib; + typedef std::tuple<Vertex_attrib, void, void, Volume_attrib> Attributes; + }; +}; +typedef CGAL::Linear_cell_complex_traits<3,Kernel> Mytraits; +typedef CGAL::Linear_cell_complex_for_combinatorial_map<3,3,Mytraits,Myitems> LCC3; + +// The last template argument CGAL::Tag_true allows to use concurent compact +// container instead of compact container => thread safe +/* typedef CGAL::Linear_cell_complex_for_combinatorial_map + <3,3,Mytraits,Myitems,CGAL_ALLOCATOR(int), + CGAL::Combinatorial_map_base, + CGAL::CMap_linear_cell_complex_storage_1 + <3, 3, Mytraits, Myitems, CGAL_ALLOCATOR(int), + // CGAL::Tag_false>> + CGAL::Tag_true>> + LCC3; */ + +typedef typename LCC3::Dart_handle Dart_handle; +typedef typename LCC3::Vertex_attribute_handle Vertex_handle; +typedef typename LCC3::Attribute_handle<3>::type Volume_handle; +typedef typename LCC3::size_type size_type; + +typedef CGAL::Polyhedron_3<Kernel> Polyhedron; +typedef CGAL::AABB_face_graph_triangle_primitive<Polyhedron> Primitive; +typedef CGAL::AABB_traits<Kernel, Primitive> Traits; +typedef CGAL::AABB_tree<Traits> Tree; +typedef typename Kernel::Triangle_3 Triangle; +typedef typename Kernel::Segment_3 Segment; +typedef CGAL::Side_of_triangle_mesh<Polyhedron, Kernel> Side_of_mesh; + +//////////////////////////////////////////////////////////////////////////////// +[[ noreturn ]] void usage(int /*argc*/, char** argv) +{ + // Name + std::cout<<"Name"<<std::endl; + std::cout<<" "<<argv[0]<<" - subdivides the given off file in hexahedra."; + std::cout<<std::endl<<std::endl; + // Synopsis + std::cout<<"SYNOPSIS"<<std::endl; + std::cout<<" "<<argv[0]<<" [--help|-h|-?] " + <<"[-create-transitions] [-draw] [-init X] [-lmax L] [-marching-cubes]" + <<"[-no-remove-outside] [-no-signature] [-save] [-smooth] " + <<"[-truncated-cubes] filename" + <<std::endl<<std::endl; + // Description + std::cout<<"DESCRIPTION"<<std::endl; + std::cout<<" "<<" subdivides the given off file in hexahedra." + <<std::endl<<std::endl; + // Options + std::cout<<" --help, -h, -?"<<std::endl + <<" display this help and exit." + <<std::endl<<std::endl; + std::cout<<" -create-transitions"<<std::endl + <<" use the 325 patterns to create transitions between hexahedra with 1 level of difference" + <<std::endl<<std::endl; + std::cout<<" -draw"<<std::endl + <<" draw the final lcc." + <<std::endl<<std::endl; + std::cout<<" -init X"<<std::endl + <<" fix the number of hexa in x axis to X (2 by default). Number of hexa in Y and Z are automatically computed to try to keep the ratio of the initial bounding box." + <<std::endl<<std::endl; + std::cout<<" -lmax L"<<std::endl + <<" gives the maximum subdvision level (4 by default)." + <<std::endl<<std::endl; + std::cout<<" -marching-cubes"<<std::endl + <<" create an approximation of the surface using marching cube method (imply !create-transition !no-remove-outside and !smooth)." + <<std::endl<<std::endl; + std::cout<<" -no-remove-outside"<<std::endl + <<" do not remove outside hexa." + <<std::endl<<std::endl; + std::cout<<" -no-signature"<<std::endl + <<" do not use signature to test isomorphism (but classical method)." + <<std::endl<<std::endl; + std::cout<<" -save"<<std::endl + <<" save the lcc resulting of the subdvision (format mesh)." + <<std::endl; + std::cout<<" -smooth"<<std::endl + <<" smooth the final volumic mesh. Contrary to marching-cubes keep all the volumes instead of creating a surface." + <<std::endl<<std::endl; + std::cout<<" -truncated-cubes"<<std::endl + <<" create a mesh with truncated cubes instead of cube (imply all hexa with same level and !marching-cubes !smooth)." + <<std::endl<<std::endl; + exit(EXIT_FAILURE); +} +//////////////////////////////////////////////////////////////////////////////// +[[ noreturn ]] void error_command_line(int argc, char** argv, const char* msg) +{ + std::cout<<"ERROR: "<<msg<<std::endl; + usage(argc, argv); +} +//////////////////////////////////////////////////////////////////////////////// +void process_command_line(int argc, char** argv, + std::string& filename, + bool& create_transitions, + bool& draw, + unsigned int& initX, + unsigned int& lmax, + bool& marching_cubes, + bool& no_remove_outside, + bool& no_signature, + bool& save, + bool& smooth, + bool& truncated_cubes + ) +{ + filename=""; + create_transitions=false; + draw=false; + initX=2; + lmax=4; + marching_cubes=false; + no_remove_outside=false; + no_signature=false; + save=false; + smooth=false; + truncated_cubes=false; + + bool helprequired=false; + std::string arg; + for (int i=1; i<argc; ++i) + { + arg=std::string(argv[i]); + if(arg==std::string("-h") || arg==std::string("--help") || arg==std::string("-?")) + { helprequired=true; } + else if(arg=="-create-transitions") + { create_transitions=true; } + else if(arg=="-draw") + { draw=true; } + else if(arg=="-init") + { + if (argc-1-i<1) + { error_command_line(argc, argv, "no numbers after -init"); } + initX=std::stoi(std::string(argv[++i])); + } + else if(arg=="-lmax") + { + if (argc-1-i<1) + { error_command_line(argc, argv, "no number after -lmax"); } + lmax=std::stoi(std::string(argv[++i])); + } + else if(arg=="-marching-cubes") + { marching_cubes=true; } + else if(arg=="-no-remove-outside") + { no_remove_outside=true; } + else if(arg=="-no-signature") + { no_signature=true; } + else if(arg=="-save") + { save=true; } + else if(arg=="-smooth") + { smooth=true; } + else if(arg=="-truncated-cubes") + { truncated_cubes=true; } + else if(arg[0]=='-') + { std::cout<<"Unknown option "<<arg<<", ignored."<<std::endl; } + else { filename=arg; } + } + if (helprequired || filename.empty()) { usage(argc, argv); } + + if(truncated_cubes) { create_transitions=false; marching_cubes=false; smooth=false; } + if(marching_cubes) { create_transitions=false; no_remove_outside=false; smooth=false; } +} +/////////////////////////////////////////////////////////////////////////////// +/* To check the intersection between a particular square (given by its four + * points) and the aabbtree t +*/ +bool is_intersect(double x1, double y1, double z1, + double x2, double y2, double z2, + double x3, double y3, double z3, + double x4, double y4, double z4, + const Tree& t) +{ + Kernel::Point_3 p1(x1,y1,z1); + Kernel::Point_3 p2(x2,y2,z2); + Kernel::Point_3 p3(x3,y3,z3); + Kernel::Point_3 p4(x4,y4,z4); + + // And compute the two triangles + Triangle t1(p1, p2, p3); + if(t.do_intersect(t1)) + { return true; } + + t1=Triangle(p1, p3, p4); + if(t.do_intersect(t1)) + { return true; } + + return false; +} +/////////////////////////////////////////////////////////////////////////////// +/* Test the intersection between a particular voxel (given by its two + * extremal points) and the aabbtree t +*/ +bool is_intersect(double x1, double y1, double z1, + double x2, double y2, double z2, + const Tree& t) +{ + return + is_intersect(x1,y1,z1, x2,y1,z1, x2,y1,z2, x1,y1,z2, t) || // f1 y1 + is_intersect(x2,y2,z1, x2,y1,z1, x2,y1,z2, x2,y2,z2, t) || // f2 x2 + is_intersect(x1,y2,z1, x2,y2,z1, x2,y2,z2, x1,y2,z2, t) || // f3 y2 + is_intersect(x1,y1,z1, x1,y1,z2, x1,y2,z2, x1,y2,z1, t) || // f4 x1 + is_intersect(x1,y1,z1, x2,y1,z1, x2,y2,z1, x1,y2,z1, t) || // f5 z1 + is_intersect(x1,y1,z2, x2,y1,z2, x2,y2,z2, x1,y2,z2, t); // f6 z2 +} +/////////////////////////////////////////////////////////////////////////////// +/* Test if a particular point is outside of the object (Tree), knowing there is + * no intersection between its voxel and the tree. + */ +bool is_outside_knowing_no_intersect(const Kernel::Point_3& p, + const Tree& t) +{ + Side_of_mesh s(t); + CGAL::Bounded_side res=s(p); + return res!=CGAL::ON_BOUNDED_SIDE; // && !=CGAL::ON_BOUNDARY ? +} +/////////////////////////////////////////////////////////////////////////////// +/* Test if a particular voxel (given by its two extremal points) is outside + * of the object (Tree). + */ +bool is_outside(double x1, double y1, double z1, + double x2, double y2, double z2, + const Tree& t) +{ + if (is_intersect(x1, y1, z1, x2, y2, z2, t)) { return false; } + return is_outside_knowing_no_intersect(Kernel::Point_3(x1, y1, z1), t); +} +/////////////////////////////////////////////////////////////////////////////// +/* Test the intersection between a particular voxel of the LCC3 + * and the Tree (variable t - the object - created from the load of a surface mesh file). + * (Rq: the variable lcc represents a set of voxels - a block of voxels of the grid). + */ +bool is_intersect(LCC3& lcc, Dart_handle dh, const Tree& t) +{ + CGAL::Bbox_3 bbox=lcc.point(dh).bbox(); + // For each vertex of the volume + for(auto it=lcc.one_dart_per_incident_cell<0,3>(dh).begin(), + itend=lcc.one_dart_per_incident_cell<0,3>(dh).end(); it!=itend; ++it) + { bbox+=lcc.point(it).bbox(); } + + return is_intersect(bbox.xmin(), bbox.ymin(), bbox.zmin(), + bbox.xmax(), bbox.ymax(), bbox.zmax(), t); +} +/////////////////////////////////////////////////////////////////////////////// +/* Test if a particular voxel of the LCC3 (lcc) is outside of the object (Tree). + * (Rq: the variable lcc represents a set of voxels - a block of voxels of the grid). + */ +bool is_outside(LCC3& lcc, Dart_handle dh, const Tree& t) +{ + CGAL::Bbox_3 bbox=lcc.point(dh).bbox(); + // For each vertex of the volume + for(auto it=lcc.one_dart_per_incident_cell<0,3>(dh).begin(), + itend=lcc.one_dart_per_incident_cell<0,3>(dh).end(); it!=itend; ++it) + { bbox+=lcc.point(it).bbox(); } + + return is_outside(bbox.xmin(), bbox.ymin(), bbox.zmin(), + bbox.xmax(), bbox.ymax(), bbox.zmax(), t); +} +//////////////////////////////////////////////////////////////////////////////// +void count_subdivisions(LCC3& lcc, std::map<std::size_t, std::size_t>& levels) +{ + levels.clear(); + unsigned int mylevel; + for(auto it=lcc.attributes<3>().begin(), itend=lcc.attributes<3>().end(); + it!=itend; ++it) + { + mylevel=lcc.info_of_attribute<3>(it); + auto l=levels.find(mylevel); + if(l==levels.end()) + { levels[mylevel]=1; } + else + { ++(l->second); } + } + for(auto it: levels) + { std::cout<<it.first<<"->"<<it.second<<" "; } + std::cout<<std::endl; +} +void count_subdivisions(LCC3& lcc) +{ + std::map<std::size_t, std::size_t> levels; + count_subdivisions(lcc, levels); +} +/////////////////////////////////////////////////////////////////////////////// +LCC3::Dart_handle make_one_hexa(LCC3& lcc, double x1, double y1, double z1, + double x2, double y2, double z2) +{ + LCC3::Dart_handle dh = lcc.make_hexahedron + (LCC3::Point(x1, y1, z1), + LCC3::Point(x2, y1, z1), + LCC3::Point(x2, y2, z1), + LCC3::Point(x1, y2, z1), + LCC3::Point(x1, y2, z2), + LCC3::Point(x1, y1, z2), + LCC3::Point(x2, y1, z2), + LCC3::Point(x2, y2, z2)); + return dh; +} +/////////////////////////////////////////////////////////////////////////////// +/// Create a block of (nbx x nby x nbz) hexa. External hexa are not created. +/// First hexa starts at position (startx, starty, starty) and each hexa has size +/// (sizex, sizey, sizez) +void create_hexa_that_intersect(LCC3& lcc, + const Tree& t, + unsigned int nbx, unsigned int nby, unsigned nbz, + double startx, double starty, double startz, + double sizex, double sizey, double sizez) +{ + Dart_handle dh; + double x1, y1, z1, x2, y2, z2; + for (unsigned int x=0; x<nbx; ++x) + { + for (unsigned int y=0; y<nby; ++y) + { + for (unsigned int z=0; z<nbz; ++z) + { + x1=startx+x*sizex; y1=starty+y*sizey; z1=startz+z*sizez; + x2=startx+(x+1)*sizex; y2=starty+(y+1)*sizey; z2=startz+(z+1)*sizez; + if (!is_outside(x1, y1, z1, x2, y2, z2, t)) + { + dh=make_one_hexa(lcc,x1, y1, z1, x2, y2, z2); + lcc.set_attribute<3>(dh, lcc.create_attribute<3>(0)); + } + } + } + } + lcc.sew3_same_facets(); +} +/////////////////////////////////////////////////////////////////////////////// +void compute_size_and_init(unsigned int init, const Tree& aabb_tree, + int& longestAxis, + double& sx, double& sy, double& sz, + unsigned int& initX, + unsigned int& initY, + unsigned int& initZ) +{ + sx=(aabb_tree.bbox().xmax()-aabb_tree.bbox().xmin()); + sy=(aabb_tree.bbox().ymax()-aabb_tree.bbox().ymin()); + sz=(aabb_tree.bbox().zmax()-aabb_tree.bbox().zmin()); + + if (sx>=sy && sx>=sz) longestAxis = 0; + else if(sy>=sx && sy>=sz) longestAxis = 1; + else longestAxis = 2; + + if(longestAxis == 0) + { + initX = init; + initY = std::ceil(init * (sy / sx)); + initZ = std::ceil(init * (sz / sx)); + } + else if(longestAxis == 1) + { + initX = std::ceil(init * (sx / sy)); + initY = init; + initZ = std::ceil(init * (sz / sy)); + } + else + { + initX = std::ceil(init * (sx / sz)); + initY = std::ceil(init * (sy / sz)); + initZ = init; + } + + sx/=initX; + sy/=initY; + sz/=initZ; +} +/////////////////////////////////////////////////////////////////////////////// +bool create_initial_voxels(const std::string& file, + LCC3& lcc, + unsigned int init, + CGAL::Polyhedron_3<Kernel>& surface, + Tree& aabb_tree) +{ + // 1) Load surface mesh + std::ifstream off_file(file); + if(!off_file.good()) return false; // File open error + + std::chrono::system_clock::time_point start2=std::chrono::system_clock::now(); + + surface.clear(); + off_file>>surface; + CGAL::Polygon_mesh_processing::triangulate_faces(surface); + + std::chrono::duration<double> diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Load off: ", diff, "s."); + + // 2) Compute AABB tree + start2 = std::chrono::system_clock::now(); + + aabb_tree.insert(faces(surface).first, faces(surface).second, surface); + aabb_tree.accelerate_distance_queries(); + aabb_tree.bbox(); + + diff = std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Compute AABB tree: ", diff, "s."); + + // std::cout<<"AABB tree bbox="<<aabb_tree.bbox()<<std::endl; + + double sx, sy, sz; + int longestAxis; // 0 = x, 1 = y, 2 = z + unsigned int initX, initY, initZ; + compute_size_and_init(init, aabb_tree, longestAxis, sx, sy, sz, + initX, initY, initZ); + + start2 = std::chrono::system_clock::now(); + + // 3) Create a block of hexahedra + create_hexa_that_intersect(lcc, aabb_tree, + initX, initY, initZ, + aabb_tree.bbox().xmin(), + aabb_tree.bbox().ymin(), + aabb_tree.bbox().zmin(), + sx, sy, sz); + + diff = std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Create block of hexa: ", diff, "s."); + + return true; +} +//////////////////////////////////////////////////////////////////////////////// +void subdivide_edges(LCC3& lcc, + const std::vector<Dart_handle>& hexa_tosubdivide, + size_type corner_mark) +{ + size_type amark=lcc.get_new_mark(); + std::vector<Dart_handle> edges_to_subdivide; + edges_to_subdivide.reserve(hexa_tosubdivide.size()); // a la louche ;) + + for(Dart_handle its: hexa_tosubdivide) + { + for(auto itd=lcc.darts_of_cell<3>(its).begin(), + itdend=lcc.darts_of_cell<3>(its).end(); itd!=itdend; ++itd) + { + if(!lcc.is_marked(itd, amark) && lcc.is_marked(itd, corner_mark) && + lcc.is_marked(lcc.beta<2>(itd), corner_mark)) + { + lcc.mark_cell<1>(itd, amark); + edges_to_subdivide.push_back(itd); + } + } + } + + for(Dart_handle itd: edges_to_subdivide) + { + lcc.unmark_cell<1>(itd, amark); + lcc.insert_barycenter_in_cell<1>(itd); + } + + assert(lcc.is_whole_map_unmarked(amark)); + lcc.free_mark(amark); +} +//////////////////////////////////////////////////////////////////////////////// +void subdivide_faces(LCC3& lcc, + const std::vector<Dart_handle>& hexa_tosubdivide, + size_type corner_mark, + Pattern_substituer<LCC3>& ps, + std::size_t& nb_replaced) +{ + size_type amark=lcc.get_new_mark(); + std::vector<Dart_handle> faces_to_subdivide; + faces_to_subdivide.reserve(hexa_tosubdivide.size()); // a la louche ;) + for(Dart_handle its: hexa_tosubdivide) + { + for(auto itd=lcc.darts_of_cell<3>(its).begin(), + itdend=lcc.darts_of_cell<3>(its).end(); itd!=itdend; ++itd) + { + if(!lcc.is_marked(itd, amark) && lcc.is_marked(itd, corner_mark) && + lcc.beta<1,1,1,1>(itd)!=itd) + { + lcc.mark_cell<2>(itd, amark); + faces_to_subdivide.push_back(itd); + } + } + } + + for(Dart_handle itd: faces_to_subdivide) + { + lcc.unmark_cell<2>(itd, amark); + /*if(ps.query_replace_one_face(lcc, itd, corner_mark)== + std::numeric_limits<std::size_t>::max()) + { std::cout<<"[ERROR] in subdivide_faces: one query/replace failed." + <<std::endl; + / *LCC3 lcc_error; + copy_cell<3>(lcc, itd, lcc_error); + for(auto itnewv=lcc.one_dart_per_incident_cell<2,3>(itd).begin(), + itnewvend=lcc.one_dart_per_incident_cell<2,3>(itd).end(); itnewv!=itnewvend; ++itnewv) + { + if(!lcc.is_free<3>(itnewv)) + { copy_cell<3>(lcc, lcc.beta<3>(itnewv), lcc_error); } + } + CGAL::draw(lcc_error); + * / + }*/ + ps.replace_one_face_from_dart(lcc, itd, ps.m_fpatterns[0], + ps.m_fsignatures.begin()->second.first); + ++nb_replaced; + } + + assert(lcc.is_whole_map_unmarked(amark)); + lcc.free_mark(amark); +} +//////////////////////////////////////////////////////////////////////////////// +void subdivide_volumes(LCC3& lcc, + const std::vector<Dart_handle>& hexa_tosubdivide, + size_type corner_mark, + Pattern_substituer<LCC3>& ps, + std::size_t& nb_replaced) +{ + std::vector<Dart_handle> vol_darts; + vol_darts.reserve(96); + Dart_handle corner_dart=nullptr; + + for(Dart_handle its: hexa_tosubdivide) + { + vol_darts.clear(); + corner_dart=nullptr; + for(auto it=lcc.darts_of_cell<3>(its).begin(), + itend=lcc.darts_of_cell<3>(its).end(); it!=itend; ++it) + { + if(!lcc.is_marked(it, corner_mark)) { vol_darts.push_back(it); } + else if(corner_dart==nullptr) { corner_dart=it; } + } + + lcc.info<3>(its)=1+lcc.info<3>(its); + /* if(ps.query_replace_one_volume(lcc, its, corner_mark)== + std::numeric_limits<std::size_t>::max()) + { std::cout<<"[ERROR] in subdivide_volumes: one query/replace failed." + <<std::endl; } */ + ps.replace_one_volume_from_dart(lcc, corner_dart, ps.m_vpatterns[0], + ps.m_vsignatures.begin()->second.first); + ++nb_replaced; + + // Mark all old darts of the 8 volume as corners + for(Dart_handle itv: vol_darts) + { + if(!lcc.is_marked(itv, corner_mark)) + { + for(auto itnewv=lcc.darts_of_cell<3>(itv).begin(), + itnewvend=lcc.darts_of_cell<3>(itv).end(); itnewv!=itnewvend; ++itnewv) + { lcc.mark(itnewv, corner_mark); } + } + } + } +} +//////////////////////////////////////////////////////////////////////////////// +void subdivide_hexa(LCC3& lcc, Dart_handle dh, LCC3::size_type corner_mark, + Pattern_substituer<LCC3>& ps) +{ + size_type mark_to_preserve_v=lcc.get_new_mark(); + mark_volume_corners<LCC3>(lcc, dh, mark_to_preserve_v); + std::vector<Dart_handle> tounmark; + //size_type mark_to_preserve_s=lcc.get_new_mark(); + std::unordered_set<Vertex_handle> vertices; + std::vector<Dart_handle> one_dart_per_corner; + one_dart_per_corner.reserve(8); + + lcc.info<3>(dh)=1+lcc.info<3>(dh); + + // 1) Subdivide each edge which is not yet subdivide + std::vector<Dart_handle> tosubdivide; + for(auto it=lcc.darts_of_cell<3>(dh).begin(), + itend=lcc.darts_of_cell<3>(dh).end(); it!=itend; ++it) + { + if(it<lcc.beta<2>(it)) // considere only one dart per edge of the hexa + { + if(lcc.is_marked(it, corner_mark) && lcc.is_marked(lcc.beta<2>(it), corner_mark)) + { tosubdivide.push_back(it); } + } + if(lcc.is_marked(it, corner_mark) && vertices.count(lcc.vertex_attribute(it))==0) + { + one_dart_per_corner.push_back(it); + vertices.insert(lcc.vertex_attribute(it)); + } + tounmark.push_back(it); + } + assert(one_dart_per_corner.size()==8); + + for(auto it: tosubdivide) + { lcc.insert_barycenter_in_cell<1>(it); } + + // 2) Subdivide faces. + tosubdivide.clear(); + for(auto it=lcc.one_dart_per_incident_cell<2,3>(dh).begin(), + itend=lcc.one_dart_per_incident_cell<2,3>(dh).end(); it!=itend; ++it) + { tosubdivide.push_back(it); } + + for(auto it: tosubdivide) + { ps.query_replace_one_face(lcc, it, mark_to_preserve_v); } + + // 3) Subdivide the hexa itself + ps.query_replace_one_volume(lcc, dh, mark_to_preserve_v); + + // 4) Mark all darts of the 8 volume as corners + for(auto itv: one_dart_per_corner) + { + std::size_t nb=0; + for(auto it=lcc.darts_of_cell<3>(itv).begin(), + itend=lcc.darts_of_cell<3>(itv).end(); it!=itend; ++it) + { lcc.mark(it, corner_mark); ++nb; } + assert(nb==24); + } + + for(auto it: tounmark) + { lcc.unmark(it, mark_to_preserve_v); } + assert(lcc.is_whole_map_unmarked(mark_to_preserve_v)); + lcc.free_mark(mark_to_preserve_v); + + // assert(lcc.is_valid()); +} +//////////////////////////////////////////////////////////////////////////////// +std::size_t compute_hexa_to_subdivide_for_truncated_cubes +(LCC3& lcc, + const Tree& aabb_tree, + bool no_remove_outside, + std::vector<std::vector<Dart_handle>>& hexa_tosubdivide) +{ + std::size_t res=0; + for(auto& it: hexa_tosubdivide) + { it.clear(); } + + std::queue<Volume_handle> totreat; + unsigned int mylevel; + for (auto it=lcc.attributes<3>().begin(); it!=lcc.attributes<3>().end(); ++it) + { + mylevel=lcc.info_of_attribute<3>(it); + if(is_intersect(lcc, lcc.dart_of_attribute<3>(it), aabb_tree)) + { + totreat.push(it); + hexa_tosubdivide[mylevel].push_back(lcc.dart_of_attribute<3>(it)); + ++res; + } + else if(!no_remove_outside && + is_outside_knowing_no_intersect + (lcc.point(lcc.dart_of_attribute<3>(it)), aabb_tree)) + { lcc.remove_cell<3>(lcc.dart_of_attribute<3>(it)); } + else // Hexa inside + { + totreat.push(it); + hexa_tosubdivide[mylevel].push_back(lcc.dart_of_attribute<3>(it)); + ++res; + } + } + return res; +} +//////////////////////////////////////////////////////////////////////////////// +std::size_t compute_hexa_to_subdivide(LCC3& lcc, + const Tree& aabb_tree, + unsigned int curlevel, + bool no_remove_outside, + bool marching_cubes, + std::vector<std::vector<Dart_handle>>& + hexa_tosubdivide) +{ + std::size_t res=0; + + for(auto& it: hexa_tosubdivide) + { it.clear(); } + + std::queue<Volume_handle> totreat; + unsigned int mylevel; + size_type treated=lcc.get_new_mark(); + for (auto it=lcc.attributes<3>().begin(); it!=lcc.attributes<3>().end(); ++it) + { + mylevel=lcc.info_of_attribute<3>(it); + if (mylevel==curlevel) + { + if(is_intersect(lcc, lcc.dart_of_attribute<3>(it), aabb_tree)) + { + totreat.push(it); + assert(lcc.dart_of_attribute<3>(it)!=nullptr); + hexa_tosubdivide[mylevel].push_back(lcc.dart_of_attribute<3>(it)); + ++res; + lcc.mark_cell<3>(lcc.dart_of_attribute<3>(it), treated); + } + else if(marching_cubes || + (!no_remove_outside && + is_outside_knowing_no_intersect + (lcc.point(lcc.dart_of_attribute<3>(it)), aabb_tree))) + { lcc.remove_cell<3>(lcc.dart_of_attribute<3>(it)); } + } + } + if(!marching_cubes) + { + Volume_handle curvh; + while(!totreat.empty()) + { + curvh=totreat.front(); + totreat.pop(); + mylevel=lcc.info_of_attribute<3>(curvh); + // Test all neighboors of curvh + for(auto it=lcc.darts_of_cell<3>(lcc.dart_of_attribute<3>(curvh)).begin(), + itend=lcc.darts_of_cell<3>(lcc.dart_of_attribute<3>(curvh)).end(); + it!=itend; ++it) + { + // Adjacency through faces AND edges ! + for(auto ite=lcc.darts_of_cell<1>(it).begin(), + iteend=lcc.darts_of_cell<1>(it).end(); ite!=iteend; ++ite) + { + if(!lcc.is_marked(ite, treated) && mylevel==1+lcc.info<3>(ite)) + { + totreat.push(lcc.attribute<3>(ite)); + hexa_tosubdivide[lcc.info<3>(ite)].push_back(ite); + ++res; + lcc.mark_cell<3>(ite, treated); + } + } + } + } + } + lcc.free_mark(treated); + return res; +} +//////////////////////////////////////////////////////////////////////////////// +void compute_marching_cubes(LCC3& lcc, size_type corner_mark, + const Tree& aabb_tree, bool no_signature) +{ + std::chrono::system_clock::time_point start=std::chrono::system_clock::now(); + std::chrono::system_clock::time_point start2=std::chrono::system_clock::now(); + Pattern_substituer<LCC3> ps2; // pattern substituer to create transitions + ps2.load_vpatterns("data/marching-cubes/vpattern/", + mark_vpattern_corners<LCC3>); + ps2.load_fpatterns("data/marching-cubes/fpattern/", + mark_fpattern_corners<LCC3>); + std::chrono::duration<double> diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Load transition patterns: ", diff, "s."); + + start2 = std::chrono::system_clock::now(); + Side_of_mesh s(aabb_tree); + for (auto it=lcc.attributes<0>().begin(); it!=lcc.attributes<0>().end(); ++it) + { + it->m_outside=(s(lcc.point_of_vertex_attribute(it))!= + CGAL::ON_BOUNDED_SIDE); //CGAL::ON_UNBOUNDED_SIDE); + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Compute inside/outside: ", diff, "s."); + + // 1) Split edges + start2 = std::chrono::system_clock::now(); + std::vector<Dart_handle> to_subdivide; + to_subdivide.reserve(lcc.number_of_attributes<3>()); // a la louche ;) + std::size_t nb_replaced_f=0; + std::size_t nb_replaced_v=0; + + for(auto itd=lcc.one_dart_per_cell<1>().begin(), + itdend=lcc.one_dart_per_cell<1>().end(); itd!=itdend; ++itd) + { + if(lcc.vertex_attribute(itd)->m_outside!= + lcc.vertex_attribute(lcc.other_extremity(itd))->m_outside) + { to_subdivide.push_back(itd); } + } + + for(Dart_handle itd: to_subdivide) + { + FT d1=std::sqrt(aabb_tree.squared_distance(lcc.point(itd))); + FT d2=std::sqrt(aabb_tree.squared_distance(lcc.point(lcc.other_extremity(itd)))); + Vector v(lcc.point(itd), lcc.point(lcc.other_extremity(itd))); + FT lg=std::sqrt(v.squared_length()); + Point p1=lcc.point(itd); + Point p2=lcc.point(lcc.other_extremity(itd)); + p1=p1.transform(Kernel::Aff_transformation_3(CGAL::TRANSLATION, (v*d1)/lg)); + p2=p2.transform(Kernel::Aff_transformation_3(CGAL::TRANSLATION, (-v*d2)/lg)); + auto newv=lcc.insert_point_in_cell<1>(itd, Point((p1.x()+p2.x())/2, + (p1.y()+p2.y())/2, + (p1.z()+p2.z())/2)); + lcc.vertex_attribute(newv)->m_outside=false; // new vertices belong to the surface => no outside + lcc.vertex_attribute(newv)->m_new_vertex=true; + // Complicated computation; but try to deal with the case where the surface + // intersect the edge several times. + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Split edges: ", diff, "s."); + + // 2) Replace faces + to_subdivide.clear(); + start2=std::chrono::system_clock::now(); + for(auto itd=lcc.one_dart_per_cell<2>().begin(), + itdend=lcc.one_dart_per_cell<2>().end(); itd!=itdend; ++itd) + { + if(lcc.beta<1,1,1,1>(itd)!=itd) + { to_subdivide.push_back(itd); } + } + if(no_signature) + { + for(auto itf: to_subdivide) + { + ps2.query_replace_one_face_without_signature(lcc, itf, corner_mark); + ++nb_replaced_f; + } + } + else + { + for(auto itf: to_subdivide) + { + ps2.query_replace_one_face(lcc, itf, corner_mark); + ++nb_replaced_f; + } + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Replace faces: ", diff, "s."); + + auto markfaces=lcc.get_new_mark(); + lcc.negate_mark(markfaces); // All darts of voxel faces are marked + + // 3) Replace volumes + to_subdivide.clear(); + start2=std::chrono::system_clock::now(); + for(auto itv=lcc.attributes<3>().begin(), + itvend=lcc.attributes<3>().end(); itv!=itvend; ++itv) + { + if(!lcc.is_volume_combinatorial_hexahedron(lcc.dart_of_attribute<3>(itv))) + { to_subdivide.push_back(lcc.dart_of_attribute<3>(itv)); } + } + if(no_signature) + { + for(auto itv: to_subdivide) + { + ps2.query_replace_one_volume_without_signature(lcc, itv, corner_mark); + ++nb_replaced_v; + } + } + else + { + for(auto itv: to_subdivide) + { + ps2.query_replace_one_volume(lcc, itv, corner_mark); + ++nb_replaced_v; + } + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Replace volumes: ", diff, "s."); + + // 4) Remove external faces + start2=std::chrono::system_clock::now(); + lcc.set_automatic_attributes_management(false); + for(auto it=lcc.darts().begin(); it!=lcc.darts().end(); ++it) + { + if(lcc.is_marked(it, markfaces)) + { + bool newface=true; + Dart_handle curdh=it; + do + { + if(!lcc.vertex_attribute(curdh)->m_new_vertex) + { newface=false; } + else + { curdh=lcc.beta<1>(curdh); } + } + while(newface && curdh!=it); + if(!newface) + { lcc.remove_cell<2>(it); } + else + { lcc.unmark_cell<2>(it, markfaces); } + } + } + assert(lcc.is_whole_map_unmarked(markfaces)); + lcc.free_mark(markfaces); + + // Now we have two volume (isomorphic) and 3-sew => remove one out of the two + // lcc.remove_cell<3>(lcc.first_dart()); // TODO PB when several cc ! + lcc.set_automatic_attributes_management(true); + + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Remove outside faces: ", diff, "s."); + + // End + diff=std::chrono::system_clock::now()-start; + print_txt_with_endl("Compute marching cube TOTAL: ", diff, "s."); + + std::cout<<"#faces-replaced: "<<nb_replaced_f<<", #volumes-replaced: "<<nb_replaced_v<<std::endl; +} +//////////////////////////////////////////////////////////////////////////////// +void smooth_mesh(LCC3& lcc, size_type corner_mark, const Tree& aabb_tree, + bool no_signature) +{ + std::chrono::system_clock::time_point start=std::chrono::system_clock::now(); + std::chrono::system_clock::time_point start2=std::chrono::system_clock::now(); + Pattern_substituer<LCC3> ps2; // pattern substituer to create transitions + ps2.load_vpatterns("data/marching-cubes/vpattern/", + mark_vpattern_corners<LCC3>); + ps2.load_fpatterns("data/marching-cubes/fpattern/", + mark_fpattern_corners<LCC3>); + std::chrono::duration<double> diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Load transition patterns: ", diff, "s."); + + start2 = std::chrono::system_clock::now(); + Side_of_mesh s(aabb_tree); + for (auto it=lcc.attributes<0>().begin(); it!=lcc.attributes<0>().end(); ++it) + { + it->m_outside=(s(lcc.point_of_vertex_attribute(it))!= + CGAL::ON_BOUNDED_SIDE); //CGAL::ON_UNBOUNDED_SIDE); + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Compute inside/outside: ", diff, "s."); + + bool mark_to_subdivide=lcc.get_new_mark(); + // 1) Split edges + start2 = std::chrono::system_clock::now(); + size_type amark=lcc.get_new_mark(); + std::vector<Dart_handle> to_subdivide; + to_subdivide.reserve(lcc.number_of_attributes<3>()); // a la louche ;) + std::size_t nb_replaced_f=0; + std::size_t nb_replaced_v=0; + + for(auto itd=lcc.darts().begin(), itdend=lcc.darts().end(); itd!=itdend; ++itd) + { + if(!lcc.is_marked(itd, amark) && + lcc.vertex_attribute(itd)->m_outside!= + lcc.vertex_attribute(lcc.other_extremity(itd))->m_outside) + { + for(auto ite=lcc.darts_of_cell_basic<1>(itd, amark).begin(), + iteend=lcc.darts_of_cell_basic<1>(itd, amark).end(); ite!=iteend; ++ite) + { + lcc.mark(ite, amark); + if(!lcc.is_marked(ite, mark_to_subdivide) && + lcc.is_volume_combinatorial_hexahedron(ite)) + { lcc.mark_cell<3>(ite, mark_to_subdivide); } + } + to_subdivide.push_back(itd); + } + } + + for(Dart_handle itd: to_subdivide) + { + lcc.unmark_cell<1>(itd, amark); + FT d1=std::sqrt(aabb_tree.squared_distance(lcc.point(itd))); + FT d2=std::sqrt(aabb_tree.squared_distance(lcc.point(lcc.other_extremity(itd)))); + Vector v(lcc.point(itd), lcc.point(lcc.other_extremity(itd))); + FT lg=std::sqrt(v.squared_length()); + Point p1=lcc.point(itd); + Point p2=lcc.point(lcc.other_extremity(itd)); + p1=p1.transform(Kernel::Aff_transformation_3(CGAL::TRANSLATION, (v*d1)/lg)); + p2=p2.transform(Kernel::Aff_transformation_3(CGAL::TRANSLATION, (-v*d2)/lg)); + auto newv=lcc.insert_point_in_cell<1>(itd, Point((p1.x()+p2.x())/2, + (p1.y()+p2.y())/2, + (p1.z()+p2.z())/2)); + lcc.vertex_attribute(newv)->m_outside=false; // new vertices belong to the surface => no outside + lcc.vertex_attribute(newv)->m_new_vertex=true; + // Complicated computation; but try to deal with the case where the surface + // intersect the edge several times. + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Split edges: ", diff, "s."); + assert(lcc.is_whole_map_unmarked(amark)); + + // 2) Replace faces + to_subdivide.clear(); + start2=std::chrono::system_clock::now(); + for(auto itd=lcc.darts().begin(), itdend=lcc.darts().end(); + itd!=itdend; ++itd) + { + if(!lcc.is_marked(itd, amark) && lcc.is_marked(itd, mark_to_subdivide) && + lcc.beta<1,1,1,1>(itd)!=itd) + { + lcc.mark_cell<2>(itd, amark); + to_subdivide.push_back(itd); + } + } + if(no_signature) + { + for(auto itf: to_subdivide) + { + lcc.unmark_cell<2>(itf, amark); + ps2.query_replace_one_face_without_signature(lcc, itf, corner_mark); + ++nb_replaced_f; + } + } + else + { + for(auto itf: to_subdivide) + { + lcc.unmark_cell<2>(itf, amark); + ps2.query_replace_one_face(lcc, itf, corner_mark); + ++nb_replaced_f; + } + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Replace faces: ", diff, "s."); + assert(lcc.is_whole_map_unmarked(amark)); + + // 3) Replace volumes + to_subdivide.clear(); + start2=std::chrono::system_clock::now(); + for(auto itd=lcc.darts().begin(), itdend=lcc.darts().end(); + itd!=itdend; ++itd) + { + if(lcc.is_marked(itd, mark_to_subdivide)) + { + for(auto itv=lcc.darts_of_cell<3>(itd).begin(), + itvend=lcc.darts_of_cell<3>(itd).end(); itv!=itvend; ++itv) + { lcc.unmark(itv, mark_to_subdivide); } + if(!lcc.is_volume_combinatorial_hexahedron(itd)) + { to_subdivide.push_back(itd); } + } + } + if(no_signature) + { + for(auto itv: to_subdivide) + { + ps2.query_replace_one_volume_without_signature(lcc, itv, corner_mark); + ++nb_replaced_v; + } + } + else + { + for(auto itv: to_subdivide) + { + ps2.query_replace_one_volume(lcc, itv, corner_mark); + ++nb_replaced_v; + } + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Replace volumes: ", diff, "s."); + assert(lcc.is_whole_map_unmarked(amark)); + lcc.free_mark(amark); + assert(lcc.is_whole_map_unmarked(mark_to_subdivide)); + lcc.free_mark(mark_to_subdivide); + + // 4) Remove external faces + start2=std::chrono::system_clock::now(); + auto markfaces=lcc.get_new_mark(); + lcc.set_automatic_attributes_management(false); + for(auto it=lcc.darts().begin(); it!=lcc.darts().end(); ++it) + { + if(!lcc.is_marked(it, markfaces)) + { + bool outside=false; + Dart_handle curdh=it; + do + { + if(lcc.vertex_attribute(curdh)->m_outside && + !lcc.vertex_attribute(curdh)->m_new_vertex) + { outside=true; } + lcc.mark(curdh, markfaces); + if(!lcc.is_free<3>(curdh)) + { lcc.mark(lcc.beta<3>(curdh), markfaces); } + curdh=lcc.beta<1>(curdh); + } + while(curdh!=it); + if(outside) + { lcc.remove_cell<2>(it); } + } + } + assert(lcc.is_whole_map_marked(markfaces)); + lcc.free_mark(markfaces); + + // Now we have two volume (isomorphic) and 3-sew => remove one out of the two + // lcc.remove_cell<3>(lcc.first_dart()); // TODO PB when several cc ! + lcc.set_automatic_attributes_management(true); + + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Remove outside faces: ", diff, "s."); + + // End + diff=std::chrono::system_clock::now()-start; + print_txt_with_endl("Compute marching cube TOTAL: ", diff, "s."); + + std::cout<<"#faces-replaced: "<<nb_replaced_f<<", #volumes-replaced: "<<nb_replaced_v<<std::endl; +} +//////////////////////////////////////////////////////////////////////////////// +void create_transition_patterns(LCC3& lcc, size_type corner_mark, + bool no_signature) +{ + std::chrono::system_clock::time_point start=std::chrono::system_clock::now(); + std::chrono::system_clock::time_point start2=std::chrono::system_clock::now(); + Pattern_substituer<LCC3> ps2; // pattern substituer to create transitions + ps2.load_vpatterns("data/hexa-325-patterns/", + mark_vpattern_corners<LCC3>); + ps2.load_fpatterns("data/square-5-patterns", + mark_fpattern_corners<LCC3>); + std::chrono::duration<double> diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Load transition patterns: ", diff, "s."); + + start2 = std::chrono::system_clock::now(); + // size_type mark_to_preserve=lcc.get_new_mark(); + std::vector<Dart_handle> faces; + std::vector<Dart_handle> volumes; + std::size_t nb_replaced_f=0; + std::size_t nb_replaced_v=0; + + cell_topo t; + for(auto it=lcc.attributes<3>().begin(); it!=lcc.attributes<3>().end(); ++it) + { + t=get_cell_topo(lcc, lcc.dart_of_attribute<3>(it)); + if(t==GENERIC_3D) + { volumes.push_back(lcc.dart_of_attribute<3>(it)); } + } + for(auto curdh: volumes) + { + // mark_volume_corners(lcc, curdh, mark_to_preserve); + faces.clear(); + for(auto itf=lcc.one_dart_per_incident_cell<2,3>(curdh).begin(), + itfend=lcc.one_dart_per_incident_cell<2,3>(curdh).end(); itf!=itfend; ++itf) + { faces.push_back(itf); } + if(no_signature) + { + for(auto itf: faces) + { + ps2.query_replace_one_face_without_signature(lcc, itf, corner_mark); + ++nb_replaced_f; + } + } + else + { + for(auto itf: faces) + { + ps2.query_replace_one_face(lcc, itf, corner_mark); + ++nb_replaced_f; + } + } + + std::size_t res; + if(no_signature) + { + res=ps2.query_replace_one_volume_without_signature(lcc, curdh, corner_mark); + ++nb_replaced_v; + } + else + { + res=ps2.query_replace_one_volume(lcc, curdh, corner_mark); + ++nb_replaced_v; + } + if(res==std::numeric_limits<std::size_t>::max()) + { + std::cout<<"[ERROR] transition not found for volume " + <<lcc.attributes<3>().index(lcc.attribute<3>(curdh)) + <<std::endl; + static unsigned int nberror=0; + LCC3 lcc_error; + copy_cell<3>(lcc, curdh, lcc_error); + CGAL::draw(lcc_error); + save_object_3D(std::string("error"+std::to_string(nberror++)+ + ".mesh"), lcc_error); + } + } + // lcc.free_mark(mark_to_preserve); + diff=std::chrono::system_clock::now()-start; + print_txt_with_endl("Create transitions TOTAL: ", diff, "s."); + + std::cout<<"#faces-replaced: "<<nb_replaced_f<<", #volumes-replaced: "<<nb_replaced_v<<std::endl; +} +//////////////////////////////////////////////////////////////////////////////// +void create_truncated_cubes(LCC3& lcc, size_type corner_mark, + bool no_signature) +{ + std::chrono::system_clock::time_point start=std::chrono::system_clock::now(); + std::chrono::system_clock::time_point start2=std::chrono::system_clock::now(); + Pattern_substituer<LCC3> ps2; // pattern substituer to create transitions + ps2.load_vpatterns("data/hexa-to-truncated-cube/vpattern/", + mark_vpattern_corners<LCC3>); + ps2.load_fpatterns("data/hexa-to-truncated-cube/fpattern/", + mark_fpattern_corners<LCC3>); + std::chrono::duration<double> diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Load transition patterns: ", diff, "s."); + + // 1) Split edges + start2 = std::chrono::system_clock::now(); + std::vector<Dart_handle> to_treat; + to_treat.reserve(lcc.number_of_attributes<3>()); // a la louche ;) + for(auto itd=lcc.one_dart_per_cell<1>().begin(), + itdend=lcc.one_dart_per_cell<1>().end(); itd!=itdend; ++itd) + { to_treat.push_back(itd); } + + for(Dart_handle itd: to_treat) + { + Dart_handle itd2=lcc.beta<2>(itd); + Vector v(lcc.point(itd), lcc.point(itd2)); + Point p1=lcc.point(itd); + Point p2=lcc.point(itd2); + p1=p1.transform(Kernel::Aff_transformation_3(CGAL::TRANSLATION, v/3.6)); + p2=p2.transform(Kernel::Aff_transformation_3(CGAL::TRANSLATION, -v/3.6)); + lcc.insert_point_in_cell<1>(itd, p1); + lcc.insert_point_in_cell<1>(itd2, p2); + } + + to_treat.clear(); + for(auto itd=lcc.one_dart_per_cell<3>().begin(), + itdend=lcc.one_dart_per_cell<3>().end(); itd!=itdend; ++itd) + { to_treat.push_back(itd); } + + std::size_t nb_replaced_f=0; + std::size_t nb_replaced_v=0; + + // size_type mark_to_preserve=lcc.get_new_mark(); + std::vector<Dart_handle> faces; + for(auto curdh: to_treat) + { + // mark_volume_corners(lcc, curdh, mark_to_preserve); + faces.clear(); + for(auto itf=lcc.one_dart_per_incident_cell<2,3>(curdh).begin(), + itfend=lcc.one_dart_per_incident_cell<2,3>(curdh).end(); itf!=itfend; ++itf) + { faces.push_back(itf); } + if(no_signature) + { + for(auto itf: faces) + { + ps2.query_replace_one_face_without_signature(lcc, itf, corner_mark); + ++nb_replaced_f; + } + } + else + { + for(auto itf: faces) + { + ps2.query_replace_one_face(lcc, itf, corner_mark); + ++nb_replaced_f; + } + } + + std::size_t res; + if(no_signature) + { + res=ps2.query_replace_one_volume_without_signature(lcc, curdh, corner_mark); + ++nb_replaced_v; + } + else + { + res=ps2.query_replace_one_volume(lcc, curdh, corner_mark); + ++nb_replaced_v; + } + if(res==std::numeric_limits<std::size_t>::max()) + { + std::cout<<"[ERROR] transition not found for volume " + <<lcc.attributes<3>().index(lcc.attribute<3>(curdh)) + <<std::endl; + static unsigned int nberror=0; + LCC3 lcc_error; + copy_cell<3>(lcc, curdh, lcc_error); + CGAL::draw(lcc_error); + save_object_3D(std::string("error"+std::to_string(nberror++)+ + ".mesh"), lcc_error); + } + } + to_treat.clear(); + for(auto itd=lcc.one_dart_per_cell<2>().begin(), + itdend=lcc.one_dart_per_cell<2>().end(); itd!=itdend; ++itd) + { + if(lcc.beta<1,1,1>(itd)==itd && !lcc.is_free<3>(itd) && + itd<lcc.beta<3>(itd) && + lcc.is_volume_combinatorial_tetrahedron(itd) && + lcc.is_volume_combinatorial_tetrahedron(lcc.beta<3>(itd))) + { to_treat.push_back(itd); } + } + for(auto itd: to_treat) + { lcc.remove_cell<2>(itd); } + + // lcc.free_mark(mark_to_preserve); + diff=std::chrono::system_clock::now()-start; + print_txt_with_endl("Create truncated cubes TOTAL: ", diff, "s."); + + std::cout<<"#faces-replaced: "<<nb_replaced_f<<", #volumes-replaced: "<<nb_replaced_v<<std::endl; +} +//////////////////////////////////////////////////////////////////////////////// +/* TODO + void chamfer_edges(LCC3& lcc, size_type corner_mark, bool no_signature) +{ + std::chrono::system_clock::time_point start=std::chrono::system_clock::now(); + std::chrono::system_clock::time_point start2=std::chrono::system_clock::now(); + + Pattern_substituer<LCC3> ps1; // pattern substituer to subdivide cube + ps1.load_fpatterns("data/hexa-8-subdivision/fpattern/", + mark_fpattern_corners<LCC3>); + ps1.load_vpatterns("data/hexa-8-subdivision/vpattern/", + mark_vpattern_corners<LCC3>); + + Pattern_substituer<LCC3> ps2; // pattern substituer to chamfer edges + ps2.load_vpatterns("data/chamfer-cube/vpatterns/", + mark_vpattern_corners<LCC3>); + ps2.load_fpatterns("data/chamfer-cube/fpatterns/", + mark_fpattern_corners<LCC3>); + + std::chrono::duration<double> diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Load transition patterns: ", diff, "s."); + + auto oldmark=lcc.get_new_mark(); + lcc.negate_mark(oldmark); // all "old" darts are marked + + // 1) Compute hexa to chamfer + start2 = std::chrono::system_clock::now(); + std::vector<Dart_handle> to_subdivide; + to_subdivide.reserve(lcc.number_of_attributes<3>()); // a la louche ;) + std::vector<Dart_handle> edges_to_chamfer; + edges_to_chamfer.reserve(lcc.number_of_attributes<3>()); // a la louche ;) + auto markvol=lcc.get_new_mark(); + + for(auto itd=lcc.one_dart_per_cell<1>().begin(), + itdend=lcc.one_dart_per_cell<1>().end(); itd!=itdend; ++itd) + { + if(CGAL::degree<LCC3, 1>(lcc, itd)==2) // Edge incident to exactly 2 faces + { + if(!lcc.is_marked(itd, markvol)) + { + to_subdivide.push_back(itd); + lcc.mark_cell<3>(itd, markvol); + } + edges_to_chamfer.push_back(itd); + edges_to_chamfer.push_back(lcc.beta<2>(itd)); + } + } + + // 2) Split hexa in 8 + subdivide_edges(lcc, to_subdivide, corner_mark); + subdivide_faces(lcc, to_subdivide, corner_mark, ps1); + subdivide_volumes(lcc, to_subdivide, corner_mark, ps1); + + // 3) Insert vertices adjacent to edges to chamfer + for(Dart_handle itd: edges_to_chamfer) + { + for(Dart_handle itn: {lcc.beta<0>(itd) / * TODO ,XXX, XXX * /}) // TODO split 4 adjacent edges + { + Vector v(lcc.point(itn), lcc.point(lcc.other_extremity(itn))); + Point p1=lcc.point(itn); + p1=p1.transform(Kernel::Aff_transformation_3(CGAL::TRANSLATION, v/6)); + lcc.insert_point_in_cell<1>(itn, p1); + } + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Split edges: ", diff, "s."); + + // 2) Replace faces + to_subdivide.clear(); + start2=std::chrono::system_clock::now(); + for(auto itd=lcc.one_dart_per_cell<2>().begin(), + itdend=lcc.one_dart_per_cell<2>().end(); itd!=itdend; ++itd) + { + if(lcc.beta<1,1,1,1>(itd)!=itd) + { to_subdivide.push_back(itd); } + } + if(no_signature) + { + for(auto itf: to_subdivide) + { ps2.query_replace_one_face_without_signature(lcc, itf, corner_mark); } + } + else + { + for(auto itf: to_subdivide) + { ps2.query_replace_one_face(lcc, itf, corner_mark); } + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Replace faces: ", diff, "s."); + + auto markfaces=lcc.get_new_mark(); + lcc.negate_mark(markfaces); // All darts of voxel faces are marked + + // 3) Replace volumes + to_subdivide.clear(); + start2=std::chrono::system_clock::now(); + for(auto itv=lcc.attributes<3>().begin(), + itvend=lcc.attributes<3>().end(); itv!=itvend; ++itv) + { + if(!lcc.is_volume_combinatorial_hexahedron(lcc.dart_of_attribute<3>(itv))) + { to_subdivide.push_back(lcc.dart_of_attribute<3>(itv)); } + } + if(no_signature) + { + for(auto itv: to_subdivide) + { ps2.query_replace_one_volume_without_signature(lcc, itv, corner_mark); } + } + else + { + for(auto itv: to_subdivide) + { ps2.query_replace_one_volume(lcc, itv, corner_mark); } + } + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Replace volumes: ", diff, "s."); + + // 4) Remove external volumes and merge 8 volumes split in 2) + start2=std::chrono::system_clock::now(); + lcc.set_automatic_attributes_management(false); + for(auto it=lcc.darts().begin(); it!=lcc.darts().end(); ++it) + { + if(lcc.is_marked(it, markfaces)) + { + bool newface=true; + Dart_handle curdh=it; + do + { + if(!lcc.vertex_attribute(curdh)->m_new_vertex) + { newface=false; } + else + { curdh=lcc.beta<1>(curdh); } + } + while(newface && curdh!=it); + if(!newface) + { lcc.remove_cell<2>(it); } + else + { lcc.unmark_cell<2>(it, markfaces); } + } + } + assert(lcc.is_whole_map_unmarked(markfaces)); + lcc.free_mark(markfaces); + + // Now we have two volume (isomorphic) and 3-sew => remove one out of the two + // lcc.remove_cell<3>(lcc.first_dart()); // TODO PB when several cc ! + lcc.set_automatic_attributes_management(true); + + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Remove outside faces: ", diff, "s."); + + // End + diff=std::chrono::system_clock::now()-start; + print_txt_with_endl("Compute marching cube TOTAL: ", diff, "s."); + + std::cout<<"#faces-replaced: "<<nb_replaced_f<<", #volumes-replaced: "<<nb_replaced_v<<std::endl; +} */ +//////////////////////////////////////////////////////////////////////////////// +bool create_mesh_from_off(const std::string& filename, + bool create_transitions, + bool draw, + unsigned int initX, + unsigned int lmax, + bool marching_cubes, + bool no_remove_outside, + bool no_signature, + bool save, + bool smooth, + bool truncated_cubes + ) +{ + LCC3 lcc; + CGAL::Polyhedron_3<Kernel> surface; + Tree aabb_tree; + size_type corner_mark=lcc.get_new_mark(); + + if (!create_initial_voxels(filename, lcc, initX, surface, aabb_tree)) + { + lcc.clear(); + return false; + } + lcc.negate_mark(corner_mark); // At the beginning, all darts are corners + + std::chrono::system_clock::time_point start=std::chrono::system_clock::now(); + std::chrono::system_clock::time_point start2=std::chrono::system_clock::now(); + + Pattern_substituer<LCC3> ps1; // pattern substituer to subdivide hexa + /*ps1.load_spatterns("data/hexa-8-subdivision/spattern/", + mark_spattern_edges<LCC3>);*/ + ps1.load_fpatterns("data/hexa-8-subdivision/fpattern/", + mark_fpattern_corners<LCC3>); + ps1.load_vpatterns("data/hexa-8-subdivision/vpattern/", + mark_vpattern_corners<LCC3>); + + bool cont=true; + unsigned int curlevel=0; //, level_of_hexa; + std::vector<std::vector<Dart_handle>> hexa_tosubdivide(lmax); + std::size_t nbtosubdivide; + std::size_t nb_replaced_f=0; + std::size_t nb_replaced_v=0; + + /*std::map<std::size_t, std::size_t> levels_to_debug1, levels_to_debug2; + count_subdivisions(lcc, levels_to_debug1); + unsigned int nb1_debug, nb2_debug;*/ + while (curlevel<lmax && cont) + { + cont=false; + nbtosubdivide=(truncated_cubes? + compute_hexa_to_subdivide_for_truncated_cubes(lcc, aabb_tree, + no_remove_outside, + hexa_tosubdivide): + compute_hexa_to_subdivide(lcc, aabb_tree, curlevel, + no_remove_outside, marching_cubes, + hexa_tosubdivide)); + if(nbtosubdivide>0) + { + /*for(std::size_t i=0; i<=curlevel; ++i) + { + for(auto it: tosubdivide[i]) + { + assert(lcc.info<3>(it)==i); + subdivide_hexa(lcc, it, corner_mark, ps1); + } + }*/ + for(std::size_t i=0; i<=curlevel; ++i) + { + subdivide_edges(lcc, hexa_tosubdivide[i], corner_mark); + subdivide_faces(lcc, hexa_tosubdivide[i], corner_mark, ps1, nb_replaced_f); + subdivide_volumes(lcc, hexa_tosubdivide[i], corner_mark, ps1, nb_replaced_v); + } + cont=true; + } + ++curlevel; + } + + std::chrono::duration<double> diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Subdivision: ", diff, "s."); + + if(!no_remove_outside) + { + start2=std::chrono::system_clock::now(); + // Longer !! lcc.set_automatic_attributes_management(false); + for (auto it=lcc.attributes<3>().begin(); it!=lcc.attributes<3>().end(); ++it) + { + if(lcc.info_of_attribute<3>(it)==lmax) + { + if((marching_cubes && + !is_intersect(lcc, lcc.dart_of_attribute<3>(it), aabb_tree)) || + (!marching_cubes && + is_outside(lcc, lcc.dart_of_attribute<3>(it), aabb_tree))) + { lcc.remove_cell<3>(lcc.dart_of_attribute<3>(it)); } + } + } + // lcc.set_automatic_attributes_management(true); + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Remove outside: ", diff, "s."); + } + + if(create_transitions) + { create_transition_patterns(lcc, corner_mark, no_signature); } + else if(marching_cubes) + { compute_marching_cubes(lcc, corner_mark, aabb_tree, no_signature); } + + if(smooth) + { smooth_mesh(lcc, corner_mark, aabb_tree, no_signature); } + + if(truncated_cubes) + { create_truncated_cubes(lcc, corner_mark, no_signature); } + + diff=std::chrono::system_clock::now()-start; + print_txt_with_endl("Create mesh TOTAL: ", diff, "s."); + std::cout<<"Final map: "; display_stats(lcc); + std::cout<<"#faces-replaced: "<<nb_replaced_f<<", #volumes-replaced: "<<nb_replaced_v<<std::endl; + assert(lcc.is_valid()); + + if(draw) + { CGAL::draw(lcc); } + + if(save) + { + std::filesystem::path p(filename); + save_object_3D(p.stem().string()+std::to_string(lmax)+".mesh", lcc); + } + + lcc.free_mark(corner_mark); + return true; +} +//////////////////////////////////////////////////////////////////////////////// +int main(int argc, char** argv) +{ + if (argc<2) // We need at least one filename + { usage(argc, argv); } + + std::string filename; + bool create_transitions; + bool draw; + unsigned int initX; + unsigned int lmax; + bool marching_cubes; + bool no_remove_outside; + bool no_signature; + bool save; + bool smooth; + bool truncated_cubes; + + process_command_line(argc, argv, + filename, + create_transitions, + draw, + initX, + lmax, + marching_cubes, + no_remove_outside, + no_signature, + save, + smooth, + truncated_cubes); + + if (!create_mesh_from_off(filename, + create_transitions, + draw, + initX, + lmax, + marching_cubes, + no_remove_outside, + no_signature, + save, + smooth, + truncated_cubes)) + { return EXIT_FAILURE; } + + return EXIT_SUCCESS; +} +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/import_moka.h b/src/import_moka.h new file mode 100644 index 0000000000000000000000000000000000000000..b0423008ebaadad6433474730a0d4e3cf258d505 --- /dev/null +++ b/src/import_moka.h @@ -0,0 +1,201 @@ +// Copyright (c) 2011 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +#ifndef IMPORT_MOKA_H +#define IMPORT_MOKA_H + +#include <vector> +#include <stack> +#include <fstream> +#include <string> +#include <iostream> +#include <cassert> + +namespace CGAL +{ +template<typename LCC> +struct GDart +{ + unsigned int alpha[4]; + typename LCC::Dart_handle dh; + typename LCC::Vertex_attribute_handle vh; + + GDart() : dh(nullptr), vh(nullptr) + {} + + GDart(const GDart& adart) : dh(adart.dh), + vh(adart.vh) + { + for (unsigned int i=0; i<4; ++i) + { alpha[i]=adart.alpha[i]; } + } +}; + +template<typename LCC> +bool import_from_moka(LCC& lcc, const char* filename) +{ + typedef typename LCC::Point Point; + + std::ifstream ifile(filename); + if (!ifile) + { + std::cout<<"Error opening file "<<filename<<"."<<std::endl; + return false; + } + + std::string line; + std::getline(ifile, line); + + if ( line == "Moka file [binary]" ) + { + std::cout<<"Binary file not (yet) considered.\n"; + return false; + } + else if ( line != "Moka file [ascii]" ) + { + std::cout<<"File "<<filename<<" is not a moka file.\n"; + std::cout<< line; + return false; + } + + // To skip the masks mark (TODO read the marks ?) + std::getline(ifile, line); + + std::vector<GDart<LCC>> gdarts; + unsigned int nbLoaded = 0; + unsigned int number; + double x,y,z; + + // First load all the gdarts, and create vertex attributes + while(ifile) + { + GDart<LCC> agdart; + ifile>>agdart.alpha[0]>>agdart.alpha[1] + >>agdart.alpha[2]>>agdart.alpha[3]; // the 4 alpha + ifile>>number>>number>>number>>number; // to skip the 4*8 marks + if ( agdart.alpha[0]==nbLoaded ) + { + std::cout<<"Impossible to load a moka file with 0-free darts.\n"; + return false; + } + if ( ifile ) + { + ifile>>number; // bool to know if dart has a vertex of not. + if (number) + { + ifile>>x>>y>>z; + agdart.vh = lcc.create_vertex_attribute(Point(x, y, z)); + } + + gdarts.push_back(agdart); + ++nbLoaded; + } + } + ifile.close(); + + // Second orient the gmap, and create oriented darts. + std::stack<unsigned int> totreat; + for (unsigned int startingdart = 0; startingdart<nbLoaded; ++startingdart) + { + bool orient=(gdarts[startingdart].dh==nullptr); + for (unsigned int dim=0; orient && dim<4; ++dim) + if (gdarts[gdarts[startingdart].alpha[dim]].dh!=nullptr) orient=false; + + if ( orient ) + { + totreat.push(startingdart); + gdarts[startingdart].dh=lcc.create_dart(); + + while ( !totreat.empty() ) + { + unsigned int i=totreat.top(); + totreat.pop(); + + assert(gdarts[i].dh!=nullptr); + + for (unsigned int dim=1; dim<4; ++dim) + { + if (gdarts[i].alpha[dim]!=i && + gdarts[gdarts[i].alpha[dim]].vh!=nullptr) + { + gdarts[i].vh = gdarts[gdarts[i].alpha[dim]].vh; + gdarts[gdarts[i].alpha[dim]].vh = nullptr; + } + + unsigned int alpha0 = gdarts[i].alpha[0]; + assert( alpha0!=i ); + + if (gdarts[alpha0].alpha[dim]!=alpha0) + { + if ( gdarts[gdarts[alpha0].alpha[dim]].dh==nullptr ) + { + totreat.push(gdarts[alpha0].alpha[dim]); + gdarts[gdarts[alpha0].alpha[dim]].dh = lcc.create_dart(); + lcc.basic_link_beta(gdarts[i].dh, + gdarts[gdarts[alpha0].alpha[dim]].dh, + dim); + } + else if (lcc.is_free(gdarts[i].dh, dim)) + { + lcc.basic_link_beta(gdarts[i].dh, + gdarts[gdarts[alpha0].alpha[dim]].dh, + dim); + } + } + } + } + } + } + + // Test that the gmap was orientable. + bool orientable = true; + for (unsigned int i = 0; i<nbLoaded; ++i) + { + if (gdarts[i].dh!=nullptr) + { + for (unsigned int dim=0; dim<4; ++dim) + { + if (orientable && + gdarts[i].alpha[dim]!=i && + gdarts[gdarts[i].alpha[dim]].dh!=nullptr) + { + std::cout<<"Pb, the gmap is NOT orientable."<<std::endl; + orientable=false; + // lcc.clear(); + } + } + + /* if ( lcc.template attribute<3>(gdarts[i].dh) == nullptr ) + { + lcc.template set_attribute<3>(gdarts[i].dh, lcc.template create_attribute<3>()); + } */ + } + if (gdarts[i].vh!=nullptr) + { + lcc.set_vertex_attribute(gdarts[i].dh, gdarts[i].vh); + } + } + + return true; +} + +} + +#endif diff --git a/src/init_to_preserve_for_query_replace.h b/src/init_to_preserve_for_query_replace.h new file mode 100644 index 0000000000000000000000000000000000000000..3cee45f3db3411c3cd67d4c4e9798f1305bbae7c --- /dev/null +++ b/src/init_to_preserve_for_query_replace.h @@ -0,0 +1,214 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef INIT_TO_PRESERVE_FOR_QUERY_REPLACE_H +#define INIT_TO_PRESERVE_FOR_QUERY_REPLACE_H + +#include"lcc_geometrical_tests.h" +#include <CGAL/Bbox_3.h> + +/////////////////////////////////////////////////////////////////////////////// +template<typename Point> +bool is_square_extremal_point(const Point& p, const CGAL::Bbox_3& b) +{ + return (b.xmin()==b.xmax() || (p.x()==b.xmin() || p.x()==b.xmax())) && + (b.ymin()==b.ymax() || (p.y()==b.ymin() || p.y()==b.ymax())) && + (b.zmin()==b.zmax() || (p.z()==b.zmin() || p.z()==b.zmax())); +} +/////////////////////////////////////////////////////////////////////////////// +template<typename Point> +bool is_edge_on_square_border(const Point& p1, const Point& p2, + const CGAL::Bbox_3& b) +{ + return (b.xmin()!=b.xmax() && p1.x()==p2.x()) || + (b.ymin()!=b.ymax() && p1.y()==p2.y()) || + (b.zmin()!=b.zmax() && p1.z()==p2.z()); +} +/////////////////////////////////////////////////////////////////////////////// +template<typename Point> +bool is_hexa_extremal_point(const Point& p, const CGAL::Bbox_3& b) +{ + return (p.x()==b.xmin() || p.x()==b.xmax()) && + (p.y()==b.ymin() || p.y()==b.ymax()) && + (p.z()==b.zmin() || p.z()==b.zmax()); +} +/////////////////////////////////////////////////////////////////////////////// +template<typename Point> +bool is_edge_on_hexa_border(const Point& p1, const Point& p2) +{ + return ((p1.x()==p2.x() && p1.y()==p2.y()) || + (p1.x()==p2.x() && p1.z()==p2.z()) || + (p1.y()==p2.y() && p1.z()==p2.z())); +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void mark_fpattern_corners(LCC& pattern, typename LCC::size_type mark_to_preserve) +{ + double epsilon=.1; + for(auto it=pattern.darts().begin(), itend=pattern.darts().end(); it!=itend; ++it) + { + if(pattern.template is_free<2>(it)) + { + typename LCC::Dart_handle prev=pattern.template beta<0>(it); + while(!pattern.template is_free<2>(prev)) + { prev=pattern.template beta<2,0>(prev); } + if(!lcc_tests::aligned(pattern.point(prev), + pattern.point(it), + pattern.point(pattern.other_extremity(it)), epsilon)) + { pattern.mark(it, mark_to_preserve); } + } + } +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void mark_face_corners(LCC& lcc, typename LCC::Dart_handle dh, + typename LCC::size_type mark_to_preserve) +{ + double epsilon=.1; + typename LCC::Dart_handle cur=dh; + do + { + if(!lcc_tests::aligned(lcc.point(lcc.beta(cur, 0)), + lcc.point(cur), + lcc.point(lcc.beta(cur, 1)), epsilon)) + { lcc.mark(cur, mark_to_preserve); } + cur=lcc.template beta<1>(cur); + } + while(cur!=dh); +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void mark_spattern_edges(LCC& pattern, typename LCC::size_type mark_border) +{ + // Mark edges between non coplanar faces + double epsilon=.1; + for(auto it=pattern.darts().begin(), itend=pattern.darts().end(); it!=itend; ++it) + { + assert(!pattern.template is_free<2>(it) && pattern.template is_free<3>(it)); + if(!pattern.is_marked(it, mark_border) && it<pattern.template beta<2>(it)) + { + typename LCC::Vector n1=CGAL::compute_normal_of_cell_2(pattern, it); + typename LCC::Vector n2=CGAL::compute_normal_of_cell_2 + (pattern, pattern.template beta<2>(it)); + if(!lcc_tests::coplanar(n1, n2, epsilon)) + { pattern.template mark_cell<1>(it, mark_border); } + } + } +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void mark_vpattern_corners(LCC& pattern, typename LCC::size_type mark_to_preserve) +{ + // 1) mark edges between non coplanar faces + // 2) mark origin of these edges having its previous marked edge non align + double epsilon=.1; + typename LCC::Dart_handle dh2; + typename LCC::size_type cube_border=pattern.get_new_mark(); + for(auto it=pattern.darts().begin(), itend=pattern.darts().end(); it!=itend; ++it) + { + if(pattern.template is_free<3>(it) && !pattern.is_marked(it, cube_border)) + { + dh2=pattern.template beta<2>(it); + while(!pattern.template is_free<3>(dh2)) + { dh2=pattern.template beta<3,2>(dh2); } + typename LCC::Vector n1=CGAL::compute_normal_of_cell_2(pattern, it); + typename LCC::Vector n2=CGAL::compute_normal_of_cell_2(pattern, dh2); + if(!lcc_tests::coplanar(n1, n2, epsilon)) + { pattern.template mark_cell<1>(it, cube_border); } + } + } + for(auto it=pattern.darts().begin(), itend=pattern.darts().end(); it!=itend; ++it) + { + if(pattern.is_marked(it, cube_border) && pattern.template is_free<3>(it)) + { + dh2=pattern.template beta<0>(it); + while(!pattern.is_marked(dh2, cube_border)) + { + dh2=pattern.template beta<2>(dh2); + while(!pattern.template is_free<3>(dh2)) + { dh2=pattern.template beta<3,2>(dh2); } + dh2=pattern.template beta<0>(dh2); + } + if(!lcc_tests::aligned(pattern.point(dh2), pattern.point(it), + pattern.point(pattern.other_extremity(it)), epsilon)) + { pattern.mark(it, mark_to_preserve); } + } + } + for(auto it=pattern.darts().begin(), itend=pattern.darts().end(); it!=itend; + ++it) + { pattern.unmark(it, cube_border); } + pattern.free_mark(cube_border); +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void mark_volume_corners(LCC& lcc, typename LCC::Dart_handle dh, + typename LCC::size_type mark_to_preserve) +{ + // 1) mark edges between non coplanar faces + // 2) mark origin of these edges having its previous marked edge non align + double epsilon=.1; + typename LCC::Dart_handle dh2; + typename LCC::size_type amark=lcc.get_new_mark(); + typename LCC::size_type cube_border=lcc.get_new_mark(); + for(auto it=lcc.template darts_of_cell_basic<3>(dh, amark).begin(), + itend=lcc.template darts_of_cell_basic<3>(dh, amark).end(); it!=itend; ++it) + { + lcc.mark(it, amark); + if(!lcc.is_marked(it, cube_border)) + { + dh2=lcc.template beta<2>(it); + typename LCC::Vector n1=CGAL::compute_normal_of_cell_2(lcc, it); + typename LCC::Vector n2=CGAL::compute_normal_of_cell_2(lcc, dh2); + if(!lcc_tests::coplanar(n1, n2, epsilon)) + { lcc.template mark_cell<1>(it, cube_border); } + } + } + lcc.negate_mark(amark); + for(auto it=lcc.template darts_of_cell_basic<3>(dh, amark).begin(), + itend=lcc.template darts_of_cell_basic<3>(dh, amark).end(); it!=itend; ++it) + { + lcc.mark(it, amark); + if(lcc.is_marked(it, cube_border)) + { + dh2=lcc.template beta<0>(it); + while(!lcc.is_marked(dh2, cube_border)) + { dh2=lcc.template beta<2,0>(dh2); } + if(!lcc_tests::aligned(lcc.point(dh2), lcc.point(it), + lcc.point(lcc.other_extremity(it)), epsilon)) + { lcc.mark(it, mark_to_preserve); } + } + } + lcc.negate_mark(amark); + + for(auto it=lcc.template darts_of_cell<3>(dh).begin(), + itend=lcc.template darts_of_cell<3>(dh).end(); it!=itend; ++it) + { + if(lcc.is_marked(it, cube_border)) + { lcc.template unmark_cell<1>(it, cube_border); } + } + + assert(lcc.is_whole_map_unmarked(amark)); + assert(lcc.is_whole_map_unmarked(cube_border)); + lcc.free_mark(amark); + lcc.free_mark(cube_border); +} +//////////////////////////////////////////////////////////////////////////////// +#endif // INIT_TO_PRESERVE_FOR_QUERY_REPLACE_H diff --git a/src/lcc_convexity_test.h b/src/lcc_convexity_test.h new file mode 100644 index 0000000000000000000000000000000000000000..e3968a273f938fffac1b0cd536847a50c36387b7 --- /dev/null +++ b/src/lcc_convexity_test.h @@ -0,0 +1,223 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef LCC_CONVEXITY_TEST_H +#define LCC_CONVEXITY_TEST_H +/////////////////////////////////////////////////////////////////////////////// +#include <CGAL/squared_distance_3.h> +#include <CGAL/Linear_cell_complex_operations.h> +/////////////////////////////////////////////////////////////////////////////// +/// @return true iff face(df) is convex +template<class LCC> +bool is_face_convex(LCC& lcc, + typename LCC::Dart_handle dh) +{ + return true; +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void mark_vertices_of_face(LCC& lcc, typename LCC::Dart_handle dh, + typename LCC::size_type mark) +{ + typename LCC::Dart_handle dh2=dh; + do + { + if(!lcc.is_marked(dh2, mark)) // Already marked => dangling or inner edge + { lcc.template mark_cell<0>(dh2, mark); } + dh2=lcc.next(dh2); + } + while(dh2!=dh); +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void unmark_vertices_of_face(LCC& lcc, typename LCC::Dart_handle dh, + typename LCC::size_type mark) +{ + lcc.negate_mark(mark); + mark_vertices_of_face(lcc, dh, mark); + lcc.negate_mark(mark); +} +/////////////////////////////////////////////////////////////////////////////// +/// @return true iff volume(df) is convex +/// Use EPSILON to test if a point belongs to a plane. +template<class LCC> +bool is_volume_convex(LCC& lcc, + typename LCC::Dart_handle dh, + const typename LCC::FT EPSILON=0.0001) +{ // TODO we can improve the test: in the current code, we test face f1 + // and its adjacent faces f2; and reciprocally for f2 we test again f1. + bool first=true; + bool positive=true; + CGAL::Oriented_side side; + auto mark=lcc.get_new_mark(); + bool on_plane=false; + + for(auto it=lcc.template one_dart_per_incident_cell<2,3>(dh).begin(), + itend=lcc.template one_dart_per_incident_cell<2,3>(dh).end(); it!=itend; ++it) + { + typename LCC::Traits::Plane_3 plane(lcc.point(it), + CGAL::compute_normal_of_cell_2(lcc, it)); + mark_vertices_of_face(lcc, it, mark); + typename LCC::Dart_handle dh2=it; + do + { + auto dh3=lcc.opposite2(dh2); + do + { + if (!lcc.is_marked(dh3, mark)) + { + on_plane=(CGAL::squared_distance(lcc.point(dh3), plane)<EPSILON); + if (!on_plane) + { + side=plane.oriented_side(lcc.point(dh3)); + assert(side!=CGAL::ON_ORIENTED_BOUNDARY); + if (first) + { + if (side==CGAL::ON_POSITIVE_SIDE) { first=false; positive=true; } + else if (side==CGAL::ON_NEGATIVE_SIDE) { first=false; positive=false; } + } + else + { + if ((side==CGAL::ON_POSITIVE_SIDE && !positive) || + (side==CGAL::ON_NEGATIVE_SIDE && positive)) + { + unmark_vertices_of_face(lcc, it, mark); + lcc.free_mark(mark); + return false; + } + } + } + } + dh3=lcc.next(dh3); + } + while(dh3!=lcc.opposite2(dh2)); + + dh2=lcc.next(dh2); + } + while(dh2!=it); + unmark_vertices_of_face(lcc, it, mark); + } + lcc.free_mark(mark); + return true; +} +/////////////////////////////////////////////////////////////////////////////// +/// @return true iff face(df) union face(beta2(dh)) is convex +template<class LCC> +bool adjacent_faces_are_convex(LCC& lcc, + typename LCC::Dart_handle dh) +{ + if(lcc.template is_free<2>(dh)) { return false; } + return true; +} +/////////////////////////////////////////////////////////////////////////////// +/// @return true iff volume(df) union volume(beta3(dh)) is convex +/// Use EPSILON to test if a point belongs to a plane. +template<class LCC> +bool adjacent_volume_are_convex(LCC& lcc, + typename LCC::Dart_handle dh, + const typename LCC::FT EPSILON=0.0001) +{ // TODO we can improve the test: in the current code, we test face f1 + // and its adjacent faces f2; and reciprocally for f2 we test again f1. + if(lcc.template is_free<3>(dh)) { return false; } + bool first=true; + bool positive=true; + CGAL::Oriented_side side; + auto mark=lcc.get_new_mark(); + auto markface=lcc.get_new_mark(); + typename LCC::Dart_handle sd=dh; + bool on_plane=false; + + lcc.template mark_cell<2>(dh, markface); + for(int i=0; i<2; ++i) + { + for(auto it=lcc.template one_dart_per_incident_cell<2,3>(sd).begin(), + itend=lcc.template one_dart_per_incident_cell<2,3>(sd).end(); it!=itend; ++it) + { + if (!lcc.is_marked(it, markface)) + { + typename LCC::Traits::Plane_3 plane(lcc.point(it), + CGAL::compute_normal_of_cell_2(lcc, it)); + mark_vertices_of_face(lcc, it, mark); + typename LCC::Dart_handle dh2=it; + do + { + typename LCC::Dart_handle sdh3=lcc.opposite2(dh2); + while(lcc.is_marked(sdh3, markface)) + { sdh3=lcc.opposite2(lcc.template opposite<3>(sdh3)); } + if(sdh3!=lcc.null_handle) + { + if(lcc.template beta<3>(sdh3)==dh2) + { // Case of future dangling face => return false + unmark_vertices_of_face(lcc, it, mark); + lcc.template unmark_cell<2>(dh, markface); + lcc.free_mark(mark); + lcc.free_mark(markface); + return false; + } + typename LCC::Dart_handle dh3=sdh3; + do + { + if (!lcc.is_marked(dh3, mark)) + { + on_plane=(CGAL::squared_distance(lcc.point(dh3), plane)<EPSILON); + if (!on_plane) + { + side=plane.oriented_side(lcc.point(dh3)); + assert(side!=CGAL::ON_ORIENTED_BOUNDARY); + if (first) + { + if (side==CGAL::ON_POSITIVE_SIDE) { first=false; positive=true; } + else if (side==CGAL::ON_NEGATIVE_SIDE) { first=false; positive=false; } + } + else + { + if ((side==CGAL::ON_POSITIVE_SIDE && !positive) || + (side==CGAL::ON_NEGATIVE_SIDE && positive)) + { + unmark_vertices_of_face(lcc, it, mark); + lcc.template unmark_cell<2>(dh, markface); + lcc.free_mark(mark); + lcc.free_mark(markface); + return false; + } + } + } + } + dh3=lcc.next(dh3); + } + while(dh3!=sdh3); + } + dh2=lcc.next(dh2); + } + while(dh2!=it); + unmark_vertices_of_face(lcc, it, mark); + } + } + sd=lcc.template opposite<3>(dh); + } + lcc.template unmark_cell<2>(dh, markface); + lcc.free_mark(mark); + lcc.free_mark(markface); + + return true; +} +/////////////////////////////////////////////////////////////////////////////// +#endif // LCC_CONVEXITY_TEST_H diff --git a/src/lcc_geometrical_tests.h b/src/lcc_geometrical_tests.h new file mode 100644 index 0000000000000000000000000000000000000000..7c5dd0e3e3fbaf13fdd220d73a75dfe8d2e96dbc --- /dev/null +++ b/src/lcc_geometrical_tests.h @@ -0,0 +1,93 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef LCC_GEOMETRICAL_TESTS_H +#define LCC_GEOMETRICAL_TESTS_H +/////////////////////////////////////////////////////////////////////////////// +#include <CGAL/Linear_cell_complex_operations.h> + +namespace lcc_tests +{ +/////////////////////////////////////////////////////////////////////////////// +enum FACE_ORIENTATION + { + ALMOST_XY, + ALMOST_XZ, + ALMOST_YZ, + ORIENTATION_UNKNOWN + }; +const double LCC_GEOMETRICAL_TESTS_EPSILON_DIST=0.000001; +const double LCC_GEOMETRICAL_TESTS_EPSILON_ANGLE=0.1; +/////////////////////////////////////////////////////////////////////////////// +bool almost_zero(double d, double epsilon=LCC_GEOMETRICAL_TESTS_EPSILON_DIST) +{ return d>=-epsilon && d<=epsilon; } +/////////////////////////////////////////////////////////////////////////////// +bool almost_non_zero(double d, double epsilon=LCC_GEOMETRICAL_TESTS_EPSILON_DIST) +{ return d<-epsilon || d>epsilon; } +/////////////////////////////////////////////////////////////////////////////// +bool almost_flat(double angle, double epsilon=LCC_GEOMETRICAL_TESTS_EPSILON_ANGLE) +{ return (angle<epsilon || angle>(360-epsilon) || + (angle>180-epsilon && angle<180+epsilon)); +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +FACE_ORIENTATION get_face_orientation(LCC& lcc, + typename LCC::Dart_handle dh, + double epsilon=LCC_GEOMETRICAL_TESTS_EPSILON_DIST) +{ + typename LCC::Vector n(CGAL::compute_normal_of_cell_2(lcc, dh)); + if(almost_zero(n.x(), epsilon) && almost_zero(n.y(), epsilon) && almost_non_zero(n.z(), epsilon)) + { return ALMOST_XY; } + if(almost_zero(n.x(), epsilon) && almost_non_zero(n.y(), epsilon) && almost_zero(n.z(), epsilon)) + { return ALMOST_XZ; } + if(almost_non_zero(n.x(), epsilon) && almost_zero(n.y(), epsilon) && almost_zero(n.z(), epsilon)) + { return ALMOST_YZ; } + return ORIENTATION_UNKNOWN; +} +/////////////////////////////////////////////////////////////////////////////// +template<typename Point> +bool aligned(const Point& p1, const Point& p2, const Point& p3, + double epsilon=LCC_GEOMETRICAL_TESTS_EPSILON_ANGLE) +{ + double angle=CGAL::approximate_angle(p1, p2, p3); + return almost_flat(angle, epsilon); +} +/////////////////////////////////////////////////////////////////////////////// +template<typename Vector> +bool coplanar(const Vector& normal1, const Vector& normal2, + double epsilon=LCC_GEOMETRICAL_TESTS_EPSILON_ANGLE) +{ + double angle=CGAL::approximate_angle(normal1, normal2); + return almost_flat(angle, epsilon); +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +CGAL::Bbox_3 compute_bbox_of_lcc(LCC& pattern) +{ + CGAL::Bbox_3 bbox=pattern.point(pattern.darts().begin()).bbox(); + for(auto it=pattern.darts().begin(), itend=pattern.darts().end(); it!=itend; ++it) + { bbox+=pattern.point(it).bbox(); } + return bbox; +} +/////////////////////////////////////////////////////////////////////////////// +} // namespace lcc_tests + +#endif // LCC_GEOMETRICAL_TESTS_H diff --git a/src/lcc_geometry_transformation.h b/src/lcc_geometry_transformation.h new file mode 100644 index 0000000000000000000000000000000000000000..9fd46d2852f50deb135f81671ad82e8ef2b23f5f --- /dev/null +++ b/src/lcc_geometry_transformation.h @@ -0,0 +1,49 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef LCC_GEOMETRY_TRANSFORMATION_H +#define LCC_GEOMETRY_TRANSFORMATION_H + +#include <CGAL/Aff_transformation_3.h> +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void lcc_translate_cc(LCC& lcc, typename LCC::Dart_handle dh, + const typename LCC::Vector& v) +{ + typename LCC::Traits::Aff_transformation_3 translate(CGAL::TRANSLATION, v); + for(auto it=lcc.template one_dart_per_incident_cell<0,4>(dh).begin(), + itend=lcc.template one_dart_per_incident_cell<0,4>(dh).end(); it!=itend; ++it) + { lcc.point(it)=translate(lcc.point(it)); } +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void lcc_translate_all(LCC& lcc, const typename LCC::Vector& v) +{ + typename LCC::Traits::Aff_transformation_3 translate(CGAL::TRANSLATION, v); + for(auto itv=lcc.vertex_attribute_range().begin(), + itvend=lcc.vertex_attribute_range().end(); itv!=itvend; ++itv) + { + lcc.point_of_vertex_attribute(itv)= + translate(lcc.point_of_vertex_attribute(itv)); + } +} +/////////////////////////////////////////////////////////////////////////////// +#endif // LCC_GEOMETRY_TRANSFORMATION_H diff --git a/src/lcc_read_depending_extension.h b/src/lcc_read_depending_extension.h new file mode 100644 index 0000000000000000000000000000000000000000..98efdd77ce6f983c136187f8b07986e9f63a4d56 --- /dev/null +++ b/src/lcc_read_depending_extension.h @@ -0,0 +1,97 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef LCC_READ_DEPENDING_EXTENSION_H +#define LCC_READ_DEPENDING_EXTENSION_H + +#include <filesystem> +#include <string> + +#include <CGAL/Linear_cell_complex_constructors.h> +#include <CGAL/Combinatorial_map_save_load.h> + +#include "import_moka.h" +#include "lcc_read_from_vtk.h" +#include "lcc_save_load_mesh.h" +#include "lcc_save_load_with_assimp.h" + +/////////////////////////////////////////////////////////////////////////////// +bool is_lcc_readable_file(const std::string& filename) +{ + std::filesystem::path p(filename); + std::string ext=p.extension().string(); + return ext==".3map" || + ext==".moka" || + ext==".off" || + ext==".mesh" || + ext==".toposim" || + ext==".vtk" || + ext==".obj" || + ext==".ply"; // More possibilities from collada ? +} +/////////////////////////////////////////////////////////////////////////////// +template<typename LCC> +typename LCC::Dart_handle read_depending_extension(const std::string& filename, + LCC& lcc) +{ + if(!is_lcc_readable_file(filename)) { return nullptr; } + + bool res=false; + typename LCC::size_type amark=LCC::INVALID_MARK; + if(!lcc.is_empty()) + { + amark=lcc.get_new_mark(); + lcc.negate_mark(amark); // All old darts are marked + } + std::filesystem::path p(filename); + std::string ext=p.extension().string(); + if(ext==".3map") + { res=CGAL::load_combinatorial_map(filename.c_str(), lcc); } + else if(ext==".moka") + { res=import_from_moka(lcc, filename.c_str()); } + else if(ext==".off") + { res=CGAL::load_off(lcc, filename.c_str()); } + else if(ext==".mesh" || ext==".toposim") + { res=load_object_3D(filename, lcc); } + else if(ext==".vtk") + { res=(read_vtk(filename, lcc)!=nullptr); } +#ifdef WITH_ASSIMP + else // By default use load with collada + { res=load_object_3D_with_assimp(filename, lcc); } +#endif // WITH_ASSIMP + + if(!res) { return nullptr; } + if(amark==LCC::INVALID_MARK) { return lcc.darts().begin(); } + + typename LCC::Dart_handle resdh=nullptr; + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + if(lcc.is_marked(it, amark)) + { + lcc.unmark(it, amark); + if(resdh==nullptr) { resdh=it; } + } + } + lcc.free_mark(amark); + return resdh; +} +/////////////////////////////////////////////////////////////////////////////// +#endif // LCC_READ_DEPENDING_EXTENSION_H diff --git a/src/lcc_read_from_vtk.h b/src/lcc_read_from_vtk.h new file mode 100644 index 0000000000000000000000000000000000000000..49b0ba2dbf56fb23888a2e4394de255e6f25ab41 --- /dev/null +++ b/src/lcc_read_from_vtk.h @@ -0,0 +1,174 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef LCC_READ_FROM_VTK_H +#define LCC_READ_FROM_VTK_H + +#include <fstream> +#include <iostream> +#include <string> +#include <vector> +#include "My_linear_cell_complex_incremental_builder.h" + +template<typename LCC> +typename LCC::Dart_handle read_vtk(const std::string& filename, LCC& lcc) +{ + std::ifstream file(filename); + if(!file.is_open()) + { + std::cerr<<"[ERROR] read_vtk: cannot open file "<<filename<<std::endl; + return nullptr; + } + + typename LCC::Dart_handle res=nullptr; + My_linear_cell_complex_incremental_builder_3<LCC> ib(lcc); + + std::size_t npoints, ncells, ncells2; + std::string line,tmp; + + while(line.find("POINTS")==std::string::npos) + { std::getline(file,line); } + std::stringstream ss(line); + std::getline(ss,tmp,' '); // skip POINTS + ss>>npoints; + + std::vector<typename LCC::Vertex_attribute_handle> points(npoints); + typename LCC::FT x,y,z; + for(std::size_t i=0; i<npoints; ++i) + { + file>>x>>y>>z; + points[i]=ib.add_vertex(typename LCC::Point(x, y, z)); + } + // std::cout<<npoints<<"\n"; + // std::cout<<points[0]<<" "<<points[1]<<" "<<points[2]<<"\n"; + + // Read Connectivity + // read until you find the CELLS line + // this is needed because meshes exported from Paraview have sometimes + // a METADATA section + while(line.find("CELLS")==std::string::npos) + { std::getline(file,line); } + ss=std::stringstream(line); + std::getline(ss,tmp,' '); // skip CELLS + ss>>ncells; + + // std::cout<<ncells<<std::endl; + + std::size_t points_per_cell; + std::vector<std::vector<std::size_t>> faces(ncells); + for(std::size_t i=0; i<ncells; ++i) + { + file>>points_per_cell; + faces[i].resize(points_per_cell); + for(std::size_t j=0; j<points_per_cell; ++j) + { file>>faces[i][j]; } + } + + while(line.find("CELL_TYPES")==std::string::npos) + { std::getline(file,line); } + ss=std::stringstream(line); + std::getline(ss,tmp,' '); // skip CELL_TYPES + ss>>ncells2; + assert(ncells==ncells2); + + std::size_t cell_type; + for(std::size_t i=0; i<ncells; ++i) + { + file>>cell_type; + //std::cout<<"cell_type "<<cell_type<<" "; + switch(cell_type) + { + case 10: // TETRA + res=make_tetrahedron_with_builder(ib, + faces[i][0], faces[i][1], + faces[i][2], faces[i][3]); + break; + case 11: // VOXEL (special case in VTK) + res=make_hexahedron_with_builder(ib, faces[i][0], faces[i][1], + faces[i][3], faces[i][2], faces[i][4], + faces[i][5], faces[i][7], faces[i][6]); + break; + case 12: // HEXA + res=make_hexahedron_with_builder(ib, faces[i][0], faces[i][1], + faces[i][2], faces[i][3], faces[i][4], + faces[i][5], faces[i][6], faces[i][7]); + break; + case 13: // PRISM (WEDGE in VTK) + res=make_prism_with_builder(ib, faces[i][0], faces[i][1], + faces[i][2], faces[i][3], faces[i][4], faces[i][5]); + break; + case 14: // PYRAMID + res=make_pyramid_with_builder(ib, faces[i][0], faces[i][1], + faces[i][2], faces[i][3], faces[i][4]); + break; + case 15: // PENTAGONAL_PRISM + res=make_pentagonal_prism_with_builder(ib, faces[i][0], faces[i][1], + faces[i][2], faces[i][3], faces[i][4], faces[i][5], + faces[i][6], faces[i][7], faces[i][8], faces[i][9]); + break; + case 16: // HEXAGONAL_PRISM + res=make_hexagonal_prism_with_builder(ib, faces[i][0], faces[i][1], + faces[i][2], faces[i][3], faces[i][4], faces[i][5], + faces[i][6], faces[i][7], faces[i][8], faces[i][9], + faces[i][10], faces[i][11]); + break; + // TODO: 24 QUADRATIC_TETRA + // 25 QUADRATIC_HEXAHEDRON + // 26 QUADRATIC_WEDGE + // 27 QUADRATIC_PYRAMID + + default: + std::cerr<<"[ERROR] read_vtk: type "<<cell_type<<" unknown."<<std::endl; + }; + //std::cout<<std::endl; + } + + for(auto itv=lcc.vertex_attributes().begin(); + itv!=lcc.vertex_attributes().end(); ++itv) + { + if(itv->dart()==nullptr) + { lcc.erase_vertex_attribute(itv); } + } + // Read weights + // Read until you find the POINTDATA line + /* while(line.find("POINT_DATA") == std::string::npos) + { std::getline(file,line); } + + std::stringstream ss1(line); + + std::size_t nweights; + std::getline(ss1,tmp,' '); // skip POINT_DATA + ss1>>nweights; + assert(nweights==npoints); + // skip next two lines + std::getline(file,line); + std::getline(file,line); + + std::vector<float> weights(npoints); + for(std::size_t i = 0 ; i < npoints; i++) + { file >> weights[i]; } + std::cout << weights[0] << "\n"; */ + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +#endif // LCC_READ_FROM_VTK_H diff --git a/src/lcc_save_load_mesh.h b/src/lcc_save_load_mesh.h new file mode 100644 index 0000000000000000000000000000000000000000..028d8e9b2aea08c96dfd0f061f2b12cb8537146c --- /dev/null +++ b/src/lcc_save_load_mesh.h @@ -0,0 +1,500 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef LCC_SAVE_LOAD_MESH_H +#define LCC_SAVE_LOAD_MESH_H + +#include "Prism_and_pyramid_creation.h" +#include "Element_topo.h" +#include <fstream> +#include <unordered_map> +#include "My_linear_cell_complex_incremental_builder.h" + +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void load_object_2D(const std::string& filename, LCC& lcc) +{ + std::ifstream fi(filename.c_str()); + if(!fi.good()) return; // File open error + + unsigned int nbparticles, nbelements, dim; + fi >> nbparticles >> nbelements >> dim; + + if(nbparticles==0 || dim!=2) + { + fi.close(); + return; + } + + typename LCC::FT x, y, z; + int index; + unsigned int nb_sommets; + std::size_t i, j; + + /*! Use incremental builder to create LCC from a 2D mesh */ + typedef My_linear_cell_complex_incremental_builder_3<LCC> + IncrementalBuilder; + IncrementalBuilder IB(lcc); + + IB.begin_surface(nbparticles, nbelements, 0); + + // Initialization of the particles + for(i=0; i<nbparticles; i++) + { + fi>>x>>y>>z; + IB.add_vertex(typename LCC::Point(x, y, z)); + } + + // Initialization of the elements + for(i=0; i<nbelements; i++) + { + fi>>nb_sommets; + IB.begin_facet(); + + for(j=0; j<nb_sommets; j++) + { + fi>>index; + IB.add_vertex_to_facet(index); + } + IB.end_facet(); + } + + IB.end_surface(); +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +bool load_object_3D(const std::string& filename, LCC& lcc) +{ + std::ifstream fi(filename.c_str()); + if(!fi.good()) return false; // File open error + + std::size_t nbparticles, nbvols, dim; + fi >> nbparticles >> nbvols >> dim; + + if(nbparticles == 0 || dim != 3) + { + fi.close(); + return false; + } + + typename LCC::FT x, y, z; + std::size_t index_element[8]; + ptrdiff_t nb_vertices_signed; + std::size_t nb_faces, nb_vertices_in_face; + std::size_t index; + + // Retrieve geometrical coordinates of particle; add vertices in an incremental builder + My_linear_cell_complex_incremental_builder_3<LCC> IB(lcc); + for(std::size_t i = 0; i < nbparticles; ++i) + { + fi >> x >> y >> z; + IB.add_vertex(typename LCC::Point(x, y, z)); + } + + for(std::size_t i = 0; i < nbvols; ++i) + { + fi >> nb_vertices_signed; + + /* Convention used in the file (the same than the one of gmesh, vtk...) + * 7----6 + * /| /| + * 4----5 | + * | 3--|-2 + * |/ |/ + * 0----1 + * + * 3 + * /|\ + * 4---5 + * | | | + * | 0 | + * |/ \| + * 1---2 + * + * 4 + * /|\ + * 0-|-3 + * | | | + * 1---2 + * + * 3 + * /|\ + * 0-|-2 + * \|/ + * 1 + */ + if(nb_vertices_signed == 4)//tetra + { + fi >> index_element[0] >> index_element[1] >> index_element[2] + >> index_element[3]; + + make_tetrahedron_with_builder(IB, index_element[0], index_element[1], + index_element[2], index_element[3]); + } + + else if(nb_vertices_signed == 5)//pyramid + { + fi >> index_element[0] >> index_element[1] >> index_element[2] + >> index_element[3] >> index_element[4]; + + make_pyramid_with_builder(IB, index_element[0], index_element[1], + index_element[2], index_element[3], index_element[4]); + } + + else if(nb_vertices_signed == 6)//prism + { + fi >> index_element[0] >> index_element[1] >> index_element[2] + >> index_element[3] >> index_element[4] >> index_element[5]; + + make_prism_with_builder(IB, index_element[0], index_element[1], + index_element[2], index_element[3], index_element[4], index_element[5]); + } + + else if(nb_vertices_signed == 8)// hexa + { + fi >> index_element[0] >> index_element[1] >> index_element[2] + >> index_element[3] >> index_element[4] >> index_element[5] + >> index_element[6] >> index_element[7]; + + make_hexahedron_with_builder(IB, index_element[0], index_element[1], + index_element[2], index_element[3], index_element[4], + index_element[5], index_element[6], index_element[7]); + } + + else if(nb_vertices_signed<0) // "generic" cell + { + fi>>nb_faces; + IB.begin_surface(); + for(std::size_t j=0; j<nb_faces; ++j) + { + fi>>nb_vertices_in_face; + IB.begin_facet(); + + for(std::size_t k=0; k<nb_vertices_in_face; ++k) + { + fi>>index; + IB.add_vertex_to_facet(index); + } + IB.end_facet(); + } + + IB.end_surface(); + } + } + + return true; +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void save_one_generic_face(LCC& lcc, typename LCC::Dart_handle dh, + std::unordered_map<typename LCC::Vertex_attribute_handle, std::size_t>& index, + std::ofstream& of) +{ + std::size_t nb=0; + typename LCC::Dart_handle cur=dh; + do + { + ++nb; + cur=lcc.next(cur); + } + while(cur!=dh); + of<<nb<<" "; + do + { + of<<index[lcc.vertex_attribute(cur)]<<" "; + cur=lcc.next(cur); + } + while(cur!=dh); +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void save_object_3D(const std::string& filename, LCC& lcc) +{ + std::ofstream fo(filename.c_str()); + if(!fo.good()) return; // File open error + + // #vertices #volumes dimension + fo<<lcc.vertex_attributes().size()<<" " + <<lcc.template one_dart_per_cell<3>().size()<<" 3"<<std::endl<<std::endl; + + std::unordered_map<typename LCC::Vertex_attribute_handle, std::size_t> index; + std::size_t nb=0; + typename LCC::Dart_handle sd; + + // all vertices + for(auto itv=lcc.vertex_attributes().begin(), + itvend=lcc.vertex_attributes().end(); itv!=itvend; ++itv) + { + fo<<itv->point()<<std::endl; + index[itv]=nb++; + } + fo<<std::endl; + + // all volumes; using indices of vertices + for(auto itvol=lcc.template one_dart_per_cell<3>().begin(), + itvolend=lcc.template one_dart_per_cell<3>().end(); + itvol!=itvolend; ++itvol) + { + /* Convention used in CGAL LCC (different than the one used in the file, cf above) + * 3 + * /|\ + * 0-|-2 + * \|/ + * 1 + * Dart incident to p0, to edge p0,p1 and to facet p0,p1,p2. + * + * 4 + * /|\ + * 0-|-3 + * | | | + * 1---2 + * Dart incident to p0 and to the facet (p0,p1,p2,p3). + * + * 3 + * /|\ + * 4---5 + * | | | + * | 0 | + * |/ \| + * 1---2 + * Dart incident to p0 and to the facet (p0,p1,p2). + * + * 7----6 + * /| /| + * 4----5 | + * | 3--|-2 + * |/ |/ + * 0----1 + * Dart incident to p0, to edge p0,p5 and to the facet (p0,p5,p6,p1). + */ + cell_topo vol_type=get_cell_topo(lcc, itvol, sd); + if(vol_type==TETRAHEDRON) + { + fo<<"4 " + <<index[lcc.vertex_attribute(sd)]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<1>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<0>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<2, 0>(sd))]<<std::endl; + } + else if(vol_type==PYRAMID) + { + fo<<"5 " + <<index[lcc.vertex_attribute(sd)]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<1>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<1,1>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<0>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<2,0>(sd))]<<std::endl; + } + else if(vol_type==PRISM) + { + fo<<"6 " + <<index[lcc.vertex_attribute(sd)]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<1>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<0>(sd))]<<" "; + + // Move to the up face + typename LCC::Dart_handle d2=lcc.template beta<2, 1, 1, 2>(sd); + fo<<index[lcc.vertex_attribute(lcc.template beta<1>(d2))]<<" " + <<index[lcc.vertex_attribute(d2)]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<0>(d2))]<<std::endl; + } + else if(vol_type==HEXAHEDRON) + { + fo<<"8 "; + // Darts associated with particles 0, 1, 2, 3 + for(unsigned int i=0; i<4; ++i) + { + fo<<index[lcc.vertex_attribute(sd)]<<" "; + sd=lcc.template beta<1>(sd); + } + typename LCC::Dart_handle d2=lcc.template beta<2, 1, 1, 2, 1>(sd); + // Darts associated with particles 4, 5, 6, 7 + for(unsigned int i = 0; i < 4; i++) + { + fo<<index[lcc.vertex_attribute(d2)]<<" "; + d2 = lcc.template beta<0>(d2); + } + fo<<std::endl; + } + else + { + // 1) number of vertices of the volume (negative number for generic cell) + // and number of faces of the volume + fo<<-static_cast<ptrdiff_t>(lcc.template one_dart_per_incident_cell<0,3,2>(sd).size())<<" " + <<(lcc.template one_dart_per_incident_cell<2,3,2>(sd).size())<<std::endl; + // 2) save each face + for(auto itface=lcc.template one_dart_per_incident_cell<2,3,2>(sd).begin(), + itfaceend=lcc.template one_dart_per_incident_cell<2,3,2>(sd).end(); + itface!=itfaceend; ++itface) + { + fo<<" "; + save_one_generic_face(lcc, itface, index, fo); + fo<<std::endl; + } + } + } + + fo.close(); +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void save_object_3D_gmsh(const std::string& filename, LCC& lcc) +{ + std::ofstream fo(filename.c_str()); + if(!fo.good()) return; // File open error + + fo<<"$MeshFormat"<<std::endl; + fo<<"2.2 0 8"<<std::endl; + fo<<"$EndMeshFormat"<<std::endl; + + fo<<"$Nodes"<<std::endl; + fo<<lcc.vertex_attributes().size()<<std::endl; + std::unordered_map<typename LCC::Vertex_attribute_handle, std::size_t> index; + std::size_t nb=0; + typename LCC::Dart_handle sd; + + for(auto itv=lcc.vertex_attributes().begin(), + itvend=lcc.vertex_attributes().end(); itv!=itvend; ++itv) + { + fo<<nb<<" "<<itv->point()<<std::endl; + index[itv]=nb++; + } + fo<<"$EndNodes"<<std::endl; + + nb=0; + fo<<"$Elements"<<std::endl; + fo<<lcc.template one_dart_per_cell<3>().size()<<std::endl; + for(auto itvol=lcc.template one_dart_per_cell<3>().begin(), + itvolend=lcc.template one_dart_per_cell<3>().end(); + itvol!=itvolend; ++itvol, ++nb) + { + cell_topo vol_type=get_cell_topo(lcc, itvol, sd); + if(vol_type==TETRAHEDRON) + { + fo<<nb<<" 4 2 0 1 " + <<index[lcc.vertex_attribute(sd)]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<1>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<0>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<2, 0>(sd))]<<std::endl; + } + else if(vol_type==PYRAMID) + { + fo<<nb<<" 7 2 0 1 " + <<index[lcc.vertex_attribute(sd)]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<1>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<1,1>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<0>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<2,0>(sd))]<<std::endl; + } + else if(vol_type==PRISM) + { + fo<<nb<<" 6 2 0 1 " + <<index[lcc.vertex_attribute(sd)]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<1>(sd))]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<0>(sd))]<<" "; + // Move to the up face + typename LCC::Dart_handle d2=lcc.template beta<2, 1, 1, 2>(sd); + fo<<index[lcc.vertex_attribute(lcc.template beta<1>(d2))]<<" " + <<index[lcc.vertex_attribute(d2)]<<" " + <<index[lcc.vertex_attribute(lcc.template beta<0>(d2))]<<std::endl; + } + else if(vol_type==HEXAHEDRON) + { + fo<<nb<<" 5 2 0 1 "; + for(unsigned int i=0; i<4; ++i) + { + fo<<index[lcc.vertex_attribute(sd)]<<" "; + sd=lcc.template beta<1>(sd); + } + typename LCC::Dart_handle d2=lcc.template beta<2, 1, 1, 2, 1>(sd); + // Darts associated with particles 4, 5, 6, 7 + for(unsigned int i = 0; i < 4; i++) + { + fo<<index[lcc.vertex_attribute(d2)]<<" "; + d2 = lcc.template beta<0>(d2); + } + fo<<std::endl; + } + else + { // TODO Generic case, not posible with gmsh format + } + } + fo<<"$EndElements"<<std::endl; +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void save_lcc_surface_into_off(const std::string& filename, LCC& lcc) +{ + std::ofstream fo(filename.c_str()); + if(!fo.good()) return; // File open error + + auto vertex_3free=lcc.get_new_mark(), face_3free=lcc.get_new_mark(); + + // count and mark all 3 free vertices and faces. + std::size_t nbvertices=0, nbfaces=0; + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + if(lcc.template is_free<3>(it)) + { + if(!lcc.is_marked(it, vertex_3free)) + { lcc.template mark_cell<0>(it, vertex_3free); ++nbvertices; } + if(!lcc.is_marked(it, face_3free)) + { lcc.template mark_cell<2,2>(it, face_3free); ++nbfaces; } + } + } + + // #vertices #faces 0 (0 to ignore number of edges) + fo<<"OFF"<<std::endl<<nbvertices<<" "<<nbfaces<<" 0"<<std::endl<<std::endl; + + std::unordered_map<typename LCC::Vertex_attribute_handle, std::size_t> index; + + // all vertices + std::size_t nb=0; + // For this loop, we cannot iterate through vertex attributes since they may + // not contain one of its dart. In such a case, it is not possible to ummark + // the 3free vertices. + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + if (lcc.is_marked(it, vertex_3free)) + { + fo<<lcc.point(it)<<std::endl; + index[lcc.vertex_attribute(it)]=nb++; + lcc.template unmark_cell<0>(it, vertex_3free); + } + } + fo<<std::endl; + + // all 3free faces + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + if (lcc.is_marked(it, face_3free)) + { + save_one_generic_face(lcc, it, index, fo); + fo<<std::endl; + lcc.template unmark_cell<2,2>(it, face_3free); + } + } + + lcc.free_mark(vertex_3free); + lcc.free_mark(face_3free); +} +/////////////////////////////////////////////////////////////////////////////// +#endif // LCC_SAVE_LOAD_MESH_H diff --git a/src/lcc_save_load_with_assimp.h b/src/lcc_save_load_with_assimp.h new file mode 100644 index 0000000000000000000000000000000000000000..a3786a43265e32e61171487dc44eed928d72fd38 --- /dev/null +++ b/src/lcc_save_load_with_assimp.h @@ -0,0 +1,186 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef LCC_SAVE_LOAD_WITH_ASSIMP_H +#define LCC_SAVE_LOAD_WITH_ASSIMP_H + +#ifdef WITH_ASSIMP + +#include <assimp/Importer.hpp> +#include <assimp/Exporter.hpp> +#include <assimp/scene.h> // Output data structure +#include <assimp/postprocess.h> // Post processing flags +#include <unordered_map> +#include "My_linear_cell_complex_incremental_builder.h" + +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +bool load_object_3D_with_assimp(const std::string& filename, LCC& lcc) +{ + Assimp::Importer importer; + importer.SetPropertyInteger(AI_CONFIG_PP_RVC_FLAGS, + aiComponent_NORMALS| + aiComponent_TANGENTS_AND_BITANGENTS| + aiComponent_COLORS| + aiComponent_TEXCOORDS| + aiComponent_BONEWEIGHTS| + aiComponent_ANIMATIONS| + aiComponent_TEXTURES| + aiComponent_LIGHTS| + aiComponent_CAMERAS| + aiComponent_MATERIALS); + const aiScene* scene=importer.ReadFile(filename, + aiProcess_RemoveComponent | + aiProcess_JoinIdenticalVertices); + if( scene==nullptr) return false; // the import failed + + // Retrieve the geometry node of a model from an input file + aiMesh** allmeshes=scene->mMeshes; + unsigned int n_meshes=scene->mNumMeshes; + + My_linear_cell_complex_incremental_builder_3<LCC> IB(lcc); + for(unsigned int m=0; m<n_meshes; ++m) + { + IB.begin_surface(); + aiMesh* mesh=allmeshes[m]; + aiVector3D* vertices=mesh->mVertices; + aiFace* faces=mesh->mFaces; + + unsigned int n_vertices=mesh->mNumVertices; + unsigned int n_faces=mesh->mNumFaces; + + // Add all vertices + for (unsigned int i=0; i<n_vertices; ++i) + { + IB.add_vertex(typename LCC::Point(vertices[i].x, + vertices[i].y, + vertices[i].z)); + } + // Add all faces + for (unsigned int i=0; i<n_faces; ++i) + { + IB.begin_facet(); + const unsigned int nbPts=faces[i].mNumIndices; + for(unsigned int j=0; j<nbPts; ++j) + { IB.add_vertex_to_facet(faces[i].mIndices[j]); } + IB.end_facet(); + } + IB.end_surface(); + } + + return true; +} +/////////////////////////////////////////////////////////////////////////////// +template<class LCC> +void save_object_3D_with_assimp(const std::string& filename, LCC& lcc) +{ + std::unordered_map<typename LCC::Vertex_attribute_handle, std::size_t> index; + + aiScene scene; + scene.mRootNode=new aiNode(); + scene.mRootNode->mMeshes=new unsigned int[1]; + scene.mRootNode->mMeshes[0]=0; + scene.mRootNode->mNumMeshes=1; + + scene.mNumMeshes=lcc.template one_dart_per_cell<3>().size(); + scene.mMeshes=new aiMesh*[scene.mNumMeshes]; + + std::size_t nbvol=0, nb=0, nb2=0; + typename LCC::Dart_handle dhcur; + + for(auto itvol=lcc.template one_dart_per_cell<3>().begin(), + itvolend=lcc.template one_dart_per_cell<3>().end(); + itvol!=itvolend; ++itvol, ++nbvol) + { + scene.mMeshes[nbvol]=new aiMesh(); + scene.mMeshes[nbvol]->mMaterialIndex=0; + + auto pMesh=scene.mMeshes[nbvol]; + pMesh->mNumVertices=lcc.template one_dart_per_incident_cell<0,3,2>(itvol).size(); + pMesh->mVertices=new aiVector3D[pMesh->mNumVertices]; + + // save each vertex + nb=0; + for(auto itv=lcc.template one_dart_per_incident_cell<0,3,2>(itvol).begin(), + itvend=lcc.template one_dart_per_incident_cell<0,3,2>(itvol).end(); + itv!=itvend; ++itv, ++nb) + { + const auto& pt=lcc.point(itv); + pMesh->mVertices[nb]=aiVector3D(pt.x(), pt.y(), pt.z()); + index[lcc.vertex_attribute(itv)]=nb; + } + + pMesh->mNumFaces=lcc.template one_dart_per_incident_cell<2,3,2>(itvol).size(); + pMesh->mFaces=new aiFace[pMesh->mNumFaces]; + + // save each face + nb=0; + for(auto itface=lcc.template one_dart_per_incident_cell<2,3,2>(itvol).begin(), + itfaceend=lcc.template one_dart_per_incident_cell<2,3,2>(itvol).end(); + itface!=itfaceend; ++itface, ++nb) + { + aiFace& face=pMesh->mFaces[nb]; + nb2=0; + dhcur=itface; + do + { ++nb2; dhcur=lcc.template beta<1>(dhcur); } + while(dhcur!=itface); + face.mNumIndices=nb2; + face.mIndices=new unsigned int[face.mNumIndices]; + dhcur=itface; nb2=0; + do + { + face.mIndices[nb2]=index[lcc.vertex_attribute(dhcur)]; + ++nb2; dhcur=lcc.template beta<1>(dhcur); + } + while(dhcur!=itface); + } + } + +/* +'assbin' *.assbin +'assxml' Assxml Document - *.assxml +'3ds' Autodesk 3DS (legacy) - *.3ds +'fbxa' Autodesk FBX (ascii) - *.fbx +'fbx' Autodesk FBX (binary) - *.fbx +'collada' COLLADA - Digital Asset Exchange Schema - *.dae +'x3d' Extensible 3D - *.x3d +'gltf' GL Transmission Format - *.gltf +'glb' GL Transmission Format (binary) - *.glb +'gltf2' GL Transmission Format v. 2 - *.gltf +'glb2' GL Transmission Format v. 2 (binary) - *.glb +'ply' Stanford Polygon Library - *.ply +'plyb' Stanford Polygon Library (binary) - *.ply +'stp' Step Files - *.stp +'stl' Stereolithography - *.stl +'stlb' Stereolithography (binary) - *.stl +'3mf' The 3MF-File-Format - *.3mf +'obj' Wavefront OBJ format - *.obj +'objnomtl' Wavefront OBJ format without material file - *.obj +'x' X Files - *.x */ + // Export(&scene, const std::string &pFormatId, const std::string &pPath); + Assimp::Exporter exporter; + // TODO exporter.Export(&scene, format, filename); +} + +#endif // WITH_ASSIMP +/////////////////////////////////////////////////////////////////////////////// +#endif // LCC_SAVE_LOAD_WITH_ASSIMP_H diff --git a/src/lcc_to_face_graph.h b/src/lcc_to_face_graph.h new file mode 100644 index 0000000000000000000000000000000000000000..e767b24949afd95a2a2db2d98ef3a86a01658f2e --- /dev/null +++ b/src/lcc_to_face_graph.h @@ -0,0 +1,95 @@ +// Copyright (c) 2011 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +//////////////////////////////////////////////////////////////////////////////// +#ifndef CGAL_LCC_TO_FACE_GRAPH_H +#define CGAL_LCC_TO_FACE_GRAPH_H + +#include <unordered_map> +#include <vector> +#include <CGAL/config.h> +#include <CGAL/iterator.h> +#include <CGAL/Kernel_traits.h> +#include <CGAL/Cartesian_converter.h> + +#include <CGAL/boost/graph/Euler_operations.h> +#include <CGAL/boost/graph/iterator.h> +#include <CGAL/boost/graph/helpers.h> +#include <CGAL/boost/graph/named_params_helper.h> +#include <CGAL/property_map.h> +#include <boost/unordered_map.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/iterator/function_output_iterator.hpp> + +namespace CGAL { + +/** Build a face graph from an LCC. Only 3-free faces are used in order to + * construct in tm only the surface of lcc. */ +template <typename LCC, typename TargetMesh> +void lcc_to_face_graph(const LCC& lcc, TargetMesh& tm) +{ + typedef typename LCC::Dart_const_handle DH; + typedef typename LCC::Vertex_attribute_const_handle VH; + + typedef typename boost::graph_traits<TargetMesh>::vertex_descriptor tm_vertex_descriptor; + + std::unordered_map<VH, tm_vertex_descriptor> vertices_map; + std::vector<tm_vertex_descriptor> vertices; + + for (auto it=lcc.vertex_attributes().begin(); + it!=lcc.vertex_attributes().end(); ++it) + { + tm_vertex_descriptor vd=CGAL::add_vertex(tm); + tm.point(vd)=it->point(); + vertices_map[it]=vd; + } + + auto treated=lcc.get_new_mark(); + for (auto it=lcc.darts().begin(); it!=lcc.darts().end(); ++it) + { + if (lcc.template is_free<3>(it) && !lcc.is_marked(it, treated)) + { + vertices.clear(); + DH cur=it; + do + { + vertices.push_back(vertices_map[lcc.vertex_attribute(cur)]); + lcc.mark(cur, treated); + cur=lcc.next(cur); + } + while(cur!=it); + + if (!CGAL::Euler::can_add_face(vertices, tm)) + { std::cerr<<"[ERROR] a face cannot be added !!"<<std::endl; } + + CGAL::Euler::add_face(vertices, tm); + } + else + { lcc.mark(it, treated); } + } + + assert(lcc.is_whole_map_marked(treated)); + lcc.free_mark(treated); +} + +} // namespace CGAL + +#endif // CGAL_LCC_TO_FACE_GRAPH_H +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/lcc_to_tetgen_io.h b/src/lcc_to_tetgen_io.h new file mode 100644 index 0000000000000000000000000000000000000000..7921fb508bfb36a90adb55e9ce087718d734b366 --- /dev/null +++ b/src/lcc_to_tetgen_io.h @@ -0,0 +1,376 @@ +// Copyright (c) 2011 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +//////////////////////////////////////////////////////////////////////////////// +#ifndef LCC_TO_TETGEN_IO_H +#define LCC_TO_TETGEN_IO_H + +#include <tetgen.h> +#include <unordered_map> +#include <vector> +#include <iostream> +//////////////////////////////////////////////////////////////////////////////// +/** Internal function that process one vertex of the lcc + */ +template <typename LCC> +void lcc_to_tetgen_process_vertex(const LCC& lcc, + typename LCC::Dart_const_handle dh, + typename LCC::size_type markv, + tetgenio& io, std::size_t nbv, + std::unordered_map + <typename LCC::Vertex_attribute_const_handle, + std::size_t>& vertices_map) +{ + io.pointlist[nbv*3]=lcc.point(dh).x(); + io.pointlist[(nbv*3)+1]=lcc.point(dh).y(); + io.pointlist[(nbv*3)+2]=lcc.point(dh).z(); + vertices_map[lcc.vertex_attribute(dh)]=nbv; + lcc.template unmark_cell<0>(dh, markv); +} +//////////////////////////////////////////////////////////////////////////////// +/** Internal function that process one face of the lcc + */ +template <typename LCC> +void lcc_to_tetgen_process_face(const LCC& lcc, + typename LCC::Dart_const_handle dh, + typename LCC::size_type markf, + tetgenio& io, std::size_t nbf, + std::unordered_map + <typename LCC::Vertex_attribute_const_handle, + std::size_t>& vertices_map) +{ + std::size_t nb=0; + typename LCC::Dart_const_handle cur=dh; + do + { + lcc.unmark(cur, markf); + ++nb; + cur=lcc.next(cur); + } + while(cur!=dh); + + tetgenio::facet *f=&io.facetlist[nbf]; + // Initialize the fields of this facet. + // There is one polygon, no hole. + f->numberofpolygons=1; + f->polygonlist=new tetgenio::polygon[1]; // Allocate memory for polygons. + f->numberofholes=0; + f->holelist=nullptr; + tetgenio::polygon *p=&f->polygonlist[0]; + p->numberofvertices=nb; + p->vertexlist=new int[nb]; // Allocate memory for vertices. + nb=0; + do + { + p->vertexlist[nb++]=vertices_map[lcc.vertex_attribute(cur)]; + cur=lcc.next(cur); + } + while(cur!=dh); +} +//////////////////////////////////////////////////////////////////////////////// +/** Build a tetgenio frwom a given volume of an LCC. + * @pre all the faces of the volume must be triangles + * (we can call constrained_delaunay_triangulation as pre-process). + */ +template <typename LCC> +void lcc_to_tetgen_one_volume_(const LCC& lcc, + typename LCC::Dart_const_handle dh, + tetgenio& io) +{ + // All indices start from 0. + io.firstnumber=0; + + // First copy vertices + std::unordered_map<typename LCC::Vertex_attribute_const_handle, std::size_t> + vertices_map; + auto markv=lcc.get_new_mark(); + auto markf=lcc.get_new_mark(); + auto treatedv=lcc.get_new_mark(); + + io.numberofpoints=0; + io.numberoffacets=0; + for(auto itvol=lcc.template darts_of_cell_basic<3>(dh, treatedv).begin(), + itvolend=lcc.template darts_of_cell_basic<3>(dh, treatedv).end(); + itvol!=itvolend; ++itvol) + { + lcc.mark(itvol, treatedv); + if(!lcc.is_marked(itvol, markv)) + { + ++(io.numberofpoints); + lcc.template mark_cell<0>(itvol, markv); + } + if(!lcc.is_marked(itvol, markf)) + { + ++(io.numberoffacets); + lcc.template mark_cell<2>(itvol, markf); + } + } + + lcc.negate_mark(treatedv); + io.pointlist=new REAL[io.numberofpoints*3]; + std::size_t nb=0; + std::vector<typename LCC::Dart_const_handle> faces; + faces.reserve(io.numberoffacets); + for(auto itvol=lcc.template darts_of_cell_basic<3>(dh, treatedv).begin(), + itvolend=lcc.template darts_of_cell_basic<3>(dh, treatedv).end(); + itvol!=itvolend; ++itvol) + { + lcc.mark(itvol, treatedv); + if(lcc.is_marked(itvol, markv)) + { lcc_to_tetgen_process_vertex(lcc, itvol, markv, io, nb++, vertices_map); } + if(lcc.is_marked(itvol, markf)) + { + faces.push_back(itvol); + lcc.template unmark_cell<2>(itvol, markf); + } + } + lcc.negate_mark(treatedv); + + io.facetlist=new tetgenio::facet[io.numberoffacets]; + nb=0; + for(auto dh: faces) + { lcc_to_tetgen_process_face(lcc, dh, markf, io, nb++, vertices_map); } + + assert(lcc.is_whole_map_unmarked(markv)); + assert(lcc.is_whole_map_unmarked(markf)); + assert(lcc.is_whole_map_unmarked(treatedv)); + lcc.free_mark(markv); + lcc.free_mark(markf); + lcc.free_mark(treatedv); +} +//////////////////////////////////////////////////////////////////////////////// +/** Build a tetgenio from an LCC, using only marked volumes + * (a volume is considered marked if one of its dart is marked) + * Face between 2 marked volumes are ignored (to satisty tetgen constraint) + * and thus only preserve the external boundary of the marked volumes. + * @pre all the faces of marked volumes must be triangles + * (we can call constrained_delaunay_triangulation as pre-process). + */ +template <typename LCC> +void lcc_to_tetgen(const LCC& lcc, typename LCC::size_type amark, tetgenio& io) +{ + typedef typename LCC::Dart_const_handle DH; + typedef typename LCC::Vertex_attribute_const_handle VH; + + // All indices start from 0. + io.firstnumber=0; + + // First copy vertices + std::unordered_map<VH, std::size_t> vertices_map; + auto markv=lcc.get_new_mark(); + auto markf=lcc.get_new_mark(); + auto treatedv=lcc.get_new_mark(); + + io.numberoffacets=0; + // 1) We mark all faces + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + if(lcc.is_marked(it, amark) && !lcc.is_marked(it, treatedv)) + { + for(auto itvol=lcc.template darts_of_cell_basic<3>(it, treatedv).begin(), + itvolend=lcc.template darts_of_cell_basic<3>(it, treatedv).end(); + itvol!=itvolend; ++itvol) + { + lcc.mark(itvol, treatedv); + if(!lcc.is_marked(itvol, markf)) + { + ++(io.numberoffacets); + lcc.template mark_cell<2,2>(itvol, markf); + } + } + } + } + + // 2) We unmark faces that are incident to two marked volumes, + // and mark and count vertices incident fo marked faces. + io.numberofpoints=0; + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + if(lcc.is_marked(it, markf)) + { + if(lcc.is_marked(lcc.template beta<3>(it), markf)) + { + lcc.template unmark_cell<2>(it, markf); + (io.numberoffacets)-=2; + } + else + { + DH cur=it; + do + { + if(!lcc.is_marked(cur, markv)) + { + ++(io.numberofpoints); + lcc.template mark_cell<0>(cur, markv); + } + cur=lcc.next(cur); + } + while(cur!=it); + } + } + } + + // 3) We create the array of points and fill it. + io.pointlist=new REAL[io.numberofpoints*3]; + std::size_t nb=0; + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + lcc.unmark(it, treatedv); + if(lcc.is_marked(it, markv)) + { lcc_to_tetgen_process_vertex(lcc, it, markv, io, nb++, vertices_map); } + } + + // 4) We create the array of faces and fill it. + io.facetlist=new tetgenio::facet[io.numberoffacets]; + nb=0; + for(auto it=lcc.darts().begin(), itend=lcc.darts().end(); it!=itend; ++it) + { + if(lcc.is_marked(it, markf)) + { lcc_to_tetgen_process_face(lcc, it, markf, io, nb++, vertices_map); } + } + + assert(lcc.is_whole_map_unmarked(markv)); + assert(lcc.is_whole_map_unmarked(markf)); + assert(lcc.is_whole_map_unmarked(treatedv)); + lcc.free_mark(markv); + lcc.free_mark(markf); + lcc.free_mark(treatedv); +} +//////////////////////////////////////////////////////////////////////////////// +/** Build a tetgenio from the entire given LCC. Preserve only the external + * surface of the volumic mesh (and not the internal faces). + * @pre all the faces of marked volumes must be triangles + * (we can call constrained_delaunay_triangulation as pre-process). + */ +template <typename LCC> +void lcc_to_tetgen(const LCC& lcc, tetgenio& io) +{ + auto amark=lcc.get_new_mark(); + lcc.negate_mark(amark); + lcc_to_tetgen(lcc, amark, io); + lcc.negate_mark(amark); +} +//////////////////////////////////////////////////////////////////////////////// +template <typename LCC> +typename LCC::Dart_handle dart_of_ith_face_of_tetra(LCC& lcc, + typename LCC::Dart_handle dh, + int i) +{ + switch(i) + { + case 3: return dh; + case 1: return lcc.template beta<2>(dh); + case 0: return lcc.template beta<1,2>(dh); + case 2: return lcc.template beta<0,2>(dh); + default: break; + } + std::cerr<<"ERROR dart_of_ith_face_of_tetra"<<std::endl; + return nullptr; +} +//////////////////////////////////////////////////////////////////////////////// +// Return the dart of the volume containing dh2 that matches dh1 +template <typename LCC> +typename LCC::Dart_handle find_dart_that_match(LCC& lcc, + typename LCC::Dart_handle dh1, + typename LCC::Dart_handle dh2) +{ + typename LCC::Vertex_attribute_handle vh1=lcc.vertex_attribute(dh1); + typename LCC::Vertex_attribute_handle vh2=lcc.vertex_attribute(lcc.next(dh1)); + typename LCC::Vertex_attribute_handle vh3=lcc.vertex_attribute(lcc.previous(dh1)); + + /*if (lcc.template attributes<0>().index(vh1)==0 || + lcc.template attributes<0>().index(vh2)==0 || + lcc.template attributes<0>().index(vh3)==0) + std::cout<<"Face to match: "<<lcc.template attributes<0>().index(vh1)<<" " + <<lcc.template attributes<0>().index(vh2)<<" " + <<lcc.template attributes<0>().index(vh3)<<std::endl;*/ + + for (auto it=lcc.template darts_of_cell<3>(dh2).begin(), + itend=lcc.template darts_of_cell<3>(dh2).end(); it!=itend; ++it) + { + /*if (lcc.template attributes<0>().index(vh1)==0 || + lcc.template attributes<0>().index(vh2)==0 || + lcc.template attributes<0>().index(vh3)==0) + std::cout<<" tested: "<<lcc.template attributes<0>().index(lcc.vertex_attribute(lcc.next(it)))<<" " + <<lcc.template attributes<0>().index(lcc.vertex_attribute(it))<<" " + <<lcc.template attributes<0>().index(lcc.vertex_attribute(lcc.previous(it)))<<std::endl;*/ + + if (vh1==lcc.vertex_attribute(lcc.next(it)) && + vh2==lcc.vertex_attribute(it) && + vh3==lcc.vertex_attribute(lcc.previous(it))) + { return it; } + } + std::cerr<<"ERROR in find_dart_that_match."<<std::endl; + return nullptr; +} +//////////////////////////////////////////////////////////////////////////////// +template <typename LCC> +void tetgen_to_lcc(const tetgenio& io, LCC& lcc) +{ + if (io.numberofcorners!=4) + { + std::cout<<"Conversion from tetgen to lcc for "<<io.numberofcorners + <<" not implemented."<<std::endl; + return; + } + + std::vector<typename LCC::Vertex_attribute_handle> TV; + TV.resize(io.numberofpoints); + for (int i=0; i<io.numberofpoints; ++i) + { + TV[i]=lcc.create_vertex_attribute(typename LCC::Point(io.pointlist[3*i], + io.pointlist[(3*i)+1], + io.pointlist[(3*i)+2])); + } + + typename LCC::Dart_handle dh, nh; + std::vector<typename LCC::Dart_handle> TH; // One dart per tetrahedra + TH.resize(io.numberoftetrahedra); + for (int i=0; i<io.numberoftetrahedra; ++i) + { + /*if (lcc.template attributes<0>().index(TV[io.tetrahedronlist[i*4]])==0 || + lcc.template attributes<0>().index(TV[io.tetrahedronlist[(i*4)+1]])==0 || + lcc.template attributes<0>().index(TV[io.tetrahedronlist[(i*4)+2]])==0 || + lcc.template attributes<0>().index(TV[io.tetrahedronlist[(i*4)+3]])==0) + std::cout<<"make_tetrahedron " + <<lcc.template attributes<0>().index(TV[io.tetrahedronlist[i*4]])<<" " + <<lcc.template attributes<0>().index(TV[io.tetrahedronlist[(i*4)+1]])<<" " + <<lcc.template attributes<0>().index(TV[io.tetrahedronlist[(i*4)+2]])<<" " + <<lcc.template attributes<0>().index(TV[io.tetrahedronlist[(i*4)+3]])<<std::endl;*/ + + TH[i]=lcc.make_tetrahedron(TV[io.tetrahedronlist[i*4]], + TV[io.tetrahedronlist[(i*4)+2]], + TV[io.tetrahedronlist[(i*4)+1]], + TV[io.tetrahedronlist[(i*4)+3]]); + for (int j=0; j<4; ++j) + { + if (io.neighborlist[(4*i)+j]!=-1 && io.neighborlist[(4*i)+j]<i) // jth neighboor of ith tetra + { // We have one dart of this tetra + dh=dart_of_ith_face_of_tetra(lcc, TH[i], j); + nh=find_dart_that_match(lcc, dh, TH[io.neighborlist[(4*i)+j]]); + assert(nh!=nullptr); + lcc.template topo_sew<3>(nh, dh); + } + } + } +} +//////////////////////////////////////////////////////////////////////////////// +#endif // CGAL_LCC_TO_TETGEN_IO_H +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/lcc_triangulate_faces.h b/src/lcc_triangulate_faces.h new file mode 100644 index 0000000000000000000000000000000000000000..8456ee7dec81a0c09ed599943f68a04f062ed8aa --- /dev/null +++ b/src/lcc_triangulate_faces.h @@ -0,0 +1,270 @@ +// Copyright (c) 2021 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// +/////////////////////////////////////////////////////////////////////////////// +#ifndef LCC_TRIANGULATE_FACES_H +#define LCC_TRIANGULATE_FACES_H + +#include <CGAL/Exact_predicates_inexact_constructions_kernel.h> +#include <CGAL/Projection_traits_3.h> +#include <CGAL/Triangulation_vertex_base_with_info_2.h> +#include <CGAL/Triangulation_face_base_with_info_2.h> +#include <CGAL/Constrained_Delaunay_triangulation_2.h> +#include <CGAL/Constrained_triangulation_plus_2.h> +#include <CGAL/Linear_cell_complex_operations.h> +/////////////////////////////////////////////////////////////////////////////// +template<typename Face_handle> +bool is_external(Face_handle fh) +{ + return fh->info().is_external; +} + +template<typename Face_handle> +int number_of_existing_edge(Face_handle fh) +{ + unsigned res=0; + for(int i=0; i<3; ++i) + { if(fh->info().exist_edge[i]) ++res; } + return res; +} + +template<typename Face_handle> +int get_free_edge(Face_handle fh) +{ + CGAL_assertion( number_of_existing_edge(fh)==2 ); + for(int i=0; i<3; ++i) + { if(!fh->info().exist_edge[i]) return i; } + + CGAL_assertion(false); + return -1; +} +/////////////////////////////////////////////////////////////////////////////// +/// Triangulate the face containing dart d1. +template<typename LCC> +void constrained_delaunay_triangulation(LCC &lcc, typename LCC::Dart_handle d1) +{ + struct Vertex_info + { + typename LCC::Dart_handle dh; + typename LCC::Vector v; + }; + + struct Face_info { + bool exist_edge[3]; + bool is_external; + bool is_process; + }; + + typedef CGAL::Projection_traits_3<CGAL::Exact_predicates_inexact_constructions_kernel> P_traits; + typedef CGAL::Triangulation_vertex_base_with_info_2<Vertex_info, P_traits> Vb; + + typedef CGAL::Triangulation_face_base_with_info_2<Face_info,P_traits> Fb1; + + typedef CGAL::Constrained_triangulation_face_base_2<P_traits, Fb1> Fb; + typedef CGAL::Triangulation_data_structure_2<Vb,Fb> TDS; + typedef CGAL::Exact_predicates_tag Itag; + typedef CGAL::Constrained_Delaunay_triangulation_2<P_traits, TDS, + Itag> CDT; + + if(lcc.template beta<1,1,1>(d1)==d1) { return; } // The face is already triangulated + + typename LCC::Vector normal=CGAL::compute_normal_of_cell_2(lcc,d1); + P_traits cdt_traits(normal); + CDT cdt(cdt_traits); + + //inserting the constraints edge by edge + typename LCC::template Dart_of_orbit_range<1>::iterator + it(lcc.template darts_of_orbit<1>(d1).begin()); + + typename CDT::Vertex_handle previous=LCC::null_handle, first=LCC::null_handle, + vh=LCC::null_handle; + + for(typename LCC::template Dart_of_orbit_range<1>::iterator + itend(lcc.template darts_of_orbit<1>(d1).end()); it!=itend; ++it) + { + vh=cdt.insert(lcc.point(it)); + vh->info().dh=it; + if(first==nullptr) + { first=vh; } + if(previous!=nullptr) + { + if(previous!=vh) + { cdt.insert_constraint(previous,vh); } + } + + previous=vh; + } + cdt.insert_constraint(previous,first); + CGAL_assertion(cdt.is_valid()); + + // sets mark is_external + for(typename CDT::All_faces_iterator fit=cdt.all_faces_begin(), + fitend=cdt.all_faces_end(); fit!=fitend; ++fit) + { + fit->info().is_external = true; + fit->info().is_process = false; + fit->info().exist_edge[0]=false; + fit->info().exist_edge[1]=false; + fit->info().exist_edge[2]=false; + } + + std::queue<typename CDT::Face_handle> face_queue; + typename CDT::Face_handle face_internal = nullptr; + + face_queue.push(cdt.infinite_vertex()->face()); + while(!face_queue.empty()) + { + typename CDT::Face_handle fh=face_queue.front(); + face_queue.pop(); + if(!fh->info().is_process) + { + fh->info().is_process = true; + for(int i = 0; i<3; ++i) + { + if(!cdt.is_constrained(std::make_pair(fh, i))) + { + face_queue.push(fh->neighbor(i)); + } + else if(face_internal==nullptr) + { + face_internal=fh->neighbor(i); + } + } + } + } + if(face_internal!=nullptr) + { face_queue.push(face_internal); } + + while(!face_queue.empty()) + { + typename CDT::Face_handle fh=face_queue.front(); + face_queue.pop(); + if(!fh->info().is_process) + { + fh->info().is_process =true; + fh->info().is_external=false; + for(int i = 0; i <3; ++i) + { + if(!cdt.is_constrained(std::make_pair(fh, i))) + { + face_queue.push(fh->neighbor(i)); + } + } + } + } + + for(typename CDT::Finite_edges_iterator eit = cdt.finite_edges_begin(), + eitend=cdt.finite_edges_end(); eit!=eitend; ++eit) + { + typename CDT::Face_handle fh=eit->first; + int index=eit->second; + typename CDT::Face_handle opposite_fh=fh->neighbor(index); + if(cdt.is_constrained(std::make_pair(fh, index))) + { + fh->info().exist_edge[index]=true; + opposite_fh->info().exist_edge[cdt.mirror_index(fh,index)]=true; + + if(!fh->info().is_external && number_of_existing_edge(fh)==2) + face_queue.push(fh); + if(!opposite_fh->info().is_external && + number_of_existing_edge(opposite_fh)==2) + face_queue.push(opposite_fh); + } + } + + while(!face_queue.empty()) + { + typename CDT::Face_handle fh=face_queue.front(); + face_queue.pop(); + CGAL_assertion(number_of_existing_edge(fh)>=2); // i.e. ==2 or ==3 + CGAL_assertion(!fh->info().is_external); + + if(number_of_existing_edge(fh)==2) + { + int index=get_free_edge(fh); + typename CDT::Face_handle opposite_fh=fh->neighbor(index); + + CGAL_assertion( !fh->info().exist_edge[index] ); + CGAL_assertion( !opposite_fh->info(). + exist_edge[cdt.mirror_index(fh,index)] ); + // triangle is (vc, vb, va) + const typename CDT::Vertex_handle va = fh->vertex(cdt. cw(index)); + const typename CDT::Vertex_handle vb = fh->vertex(cdt.ccw(index)); + const typename CDT::Vertex_handle vc = fh->vertex(index); + + typename LCC::Dart_handle dd1 = nullptr; + for(typename LCC::template Dart_of_cell_range<0, 2>::iterator + iti=lcc.template darts_of_cell<0, 2>(va->info().dh).begin(); + dd1==nullptr && iti.cont(); ++iti) + { + if(lcc.point(lcc.template beta<1>(iti))==vc->point()) + { dd1=iti; } + } + + typename LCC::Dart_handle dd2 = nullptr; + for(typename LCC::template Dart_of_cell_range<0, 2>::iterator + iti=lcc.template darts_of_cell<0, 2>(vb->info().dh).begin(); + dd2==nullptr && iti.cont(); ++iti) + { + if(lcc.point(lcc.template beta<0>(iti))==vc->point()) + { dd2=iti; } + } + + // assert(((lcc.beta<0,0>(dd1)==dd2) || lcc.beta<1,1>(dd1)==dd2)); + + typename LCC::Dart_handle ndart=lcc.insert_cell_1_in_cell_2(dd1, dd2); + va->info().dh=lcc.template beta<2>(ndart); + + fh->info().exist_edge[index]=true; + opposite_fh->info().exist_edge[cdt.mirror_index(fh,index)]=true; + + if(!opposite_fh->info().is_external && + number_of_existing_edge(opposite_fh)==2) + { face_queue.push(opposite_fh); } + } + } +} +/////////////////////////////////////////////////////////////////////////////// +/// Triangulate all marked faces (each face having at least one marked dart) +template<typename LCC> +void triangulate_marked_faces(LCC &lcc, typename LCC::size_type amark) +{ + // We are going to call constrained_delaunay_triangulation several time for + // a same face when it has all its darts marked. But only the first call will + // triangulate the face, the other ones will do nothing since the faces are + // already triangulated. It is maybe faster to mark darts of the face in order + // to avoid these successive calls, but not sure since we must unmark the + // marked darts in another loop. + for(auto it=lcc.darts().begin(); it!=lcc.darts().end(); ++it) + { + if(lcc.is_marked(it, amark)) + { constrained_delaunay_triangulation(lcc, it); } + } +} +/////////////////////////////////////////////////////////////////////////////// +/// Triangulate all faces of the lcc. +template<typename LCC> +void triangulate_all_faces(LCC &lcc) +{ + for(auto it=lcc.darts().begin(); it!=lcc.darts().end(); ++it) + { constrained_delaunay_triangulation(lcc, it); } +} +/////////////////////////////////////////////////////////////////////////////// +#endif // LCC_TRIANGULATE_FACES_H diff --git a/src/tetra-to-hexa.cpp b/src/tetra-to-hexa.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6ef65e08cfb57b4972729b0aa4e28a9f895e34c4 --- /dev/null +++ b/src/tetra-to-hexa.cpp @@ -0,0 +1,264 @@ +// Copyright (c) 2022 CNRS and LIRIS' Establishments (France). +// All rights reserved. +// +// This file is part of LCC-Demo. +// +// LCC-Demo is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Foobar is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with LCC-Demo. If not, see <https://www.gnu.org/licenses/>. +// +// Author(s) : Guillaume Damiand <guillaume.damiand@liris.cnrs.fr> +// + +// Prog that uses the query/replace method to generate transitions +// for hexaedral meshes (based on the paper "Fast Quadtree/Octree adaptive +// meshing and re-meshing with linear mixed elements"). +// It uses the 325 vpatterns in hexa-325-patterns +// and the 5 fpatterns in square-5-patterns + +#include <CGAL/Linear_cell_complex_for_combinatorial_map.h> +#include <CGAL/Linear_cell_complex_operations.h> +#include <CGAL/Exact_predicates_inexact_constructions_kernel.h> +#include <CGAL/draw_linear_cell_complex.h> +#include <CGAL/Polyhedron_3.h> +#include <CGAL/AABB_tree.h> +#include <CGAL/AABB_traits.h> +#include <CGAL/AABB_face_graph_triangle_primitive.h> +#include <CGAL/Side_of_triangle_mesh.h> +#include <CGAL/Polygon_mesh_processing/triangulate_faces.h> +#include <CGAL/Aff_transformation_3.h> +#include <CGAL/aff_transformation_tags.h> + +#include <map> +#include <sys/types.h> +#include <unistd.h> + +#include "cmap_copy.h" +#include "cmap_query_replace.h" +#include "Compute_stats.h" +#include "init_to_preserve_for_query_replace.h" +#include "lcc_read_depending_extension.h" +#include "lcc_save_load_mesh.h" +#include "Print_txt.h" +#include "Tetrahedral_tools.h" + +//////////////////////////////////////////////////////////////////////////////// +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::FT FT; +typedef Kernel::Point_3 Point; +typedef Kernel::Vector_3 Vector; +typedef CGAL::Linear_cell_complex_for_combinatorial_map<3,3> LCC3; +typedef typename LCC3::Dart_handle Dart_handle; +typedef typename LCC3::Vertex_attribute_handle Vertex_handle; +typedef typename LCC3::size_type size_type; + +//////////////////////////////////////////////////////////////////////////////// +[[ noreturn ]] void usage(int /*argc*/, char** argv) +{ + // Name + std::cout<<"Name"<<std::endl; + std::cout<<" "<<argv[0]<<" - subdivides the given off file in hexahedra."; + std::cout<<std::endl<<std::endl; + // Synopsis + std::cout<<"SYNOPSIS"<<std::endl; + std::cout<<" "<<argv[0]<<" [--help|-h|-?] " + <<"[-draw] [-save] filename" + <<std::endl<<std::endl; + // Description + std::cout<<"DESCRIPTION"<<std::endl; + std::cout<<" "<<" subdivides the given off file in hexahedra, by first computing a tetrahedral mesh, then transform all tetrahedra into hexahedra." + <<std::endl<<std::endl; + // Options + std::cout<<" --help, -h, -?"<<std::endl + <<" display this help and exit." + <<std::endl<<std::endl; + std::cout<<" -draw"<<std::endl + <<" draw the final lcc." + <<std::endl<<std::endl; + std::cout<<" -save"<<std::endl + <<" save the lcc resulting of the subdvision (format mesh)." + <<std::endl; + exit(EXIT_FAILURE); +} +//////////////////////////////////////////////////////////////////////////////// +[[ noreturn ]] void error_command_line(int argc, char** argv, const char* msg) +{ + std::cout<<"ERROR: "<<msg<<std::endl; + usage(argc, argv); +} +//////////////////////////////////////////////////////////////////////////////// +void process_command_line(int argc, char** argv, + std::string& filename, + bool& draw, + bool& save + ) +{ + filename=""; + draw=false; + save=false; + + bool helprequired=false; + std::string arg; + for (int i=1; i<argc; ++i) + { + arg=std::string(argv[i]); + if(arg==std::string("-h") || arg==std::string("--help") || arg==std::string("-?")) + { helprequired=true; } + else if(arg=="-draw") + { draw=true; } + else if(arg=="-save") + { save=true; } + else if(arg[0]=='-') + { std::cout<<"Unknown option "<<arg<<", ignored."<<std::endl; } + else { filename=arg; } + } + if (helprequired || filename.empty()) { usage(argc, argv); } +} +//////////////////////////////////////////////////////////////////////////////// +void subdivide_edges(LCC3& lcc) +{ + std::vector<Dart_handle> edges_to_subdivide; + edges_to_subdivide.reserve(lcc.number_of_darts()/3); // a la louche ;) + + for(auto itd=lcc.one_dart_per_cell<1>().begin(), + itdend=lcc.one_dart_per_cell<1>().end(); itd!=itdend; ++itd) + { edges_to_subdivide.push_back(itd); } + + for(Dart_handle itd: edges_to_subdivide) + { lcc.insert_barycenter_in_cell<1>(itd); } +} +//////////////////////////////////////////////////////////////////////////////// +void subdivide_faces(LCC3& lcc, + size_type corner_mark, + Pattern_substituer<LCC3>& ps) +{ + std::vector<Dart_handle> faces_to_subdivide; + faces_to_subdivide.reserve(lcc.number_of_darts()/9); // a la louche ;) + for(auto itd=lcc.one_dart_per_cell<2>().begin(), + itdend=lcc.one_dart_per_cell<2>().end(); itd!=itdend; ++itd) + { + if(lcc.is_marked(itd, corner_mark)) + { faces_to_subdivide.push_back(itd); } + else + { + assert(lcc.is_marked(lcc.beta<0>(itd), corner_mark)); + faces_to_subdivide.push_back(lcc.beta<0>(itd)); + } + } + + for(Dart_handle itd: faces_to_subdivide) + { + ps.replace_one_face_from_dart(lcc, itd, ps.m_fpatterns[0], + ps.m_fsignatures.begin()->second.first); + } +} +//////////////////////////////////////////////////////////////////////////////// +void subdivide_volumes(LCC3& lcc, + size_type corner_mark, + Pattern_substituer<LCC3>& ps) +{ + std::vector<Dart_handle> vols_to_subdivide; + vols_to_subdivide.reserve(lcc.number_of_darts()/24); + for(auto itd=lcc.one_dart_per_cell<3>().begin(), + itdend=lcc.one_dart_per_cell<3>().end(); itd!=itdend; ++itd) + { + if(lcc.is_marked(itd, corner_mark)) + { vols_to_subdivide.push_back(itd); } + else + { + assert(lcc.is_marked(lcc.beta<0>(itd), corner_mark)); + vols_to_subdivide.push_back(lcc.beta<0>(itd)); + } + } + + for(Dart_handle itd: vols_to_subdivide) + { + ps.replace_one_volume_from_dart(lcc, itd, ps.m_vpatterns[0], + ps.m_vsignatures.begin()->second.first); + } +} +//////////////////////////////////////////////////////////////////////////////// +bool create_mesh_from_off(const std::string& filename, + bool draw, + bool save + ) +{ + std::chrono::system_clock::time_point start=std::chrono::system_clock::now(); + std::chrono::system_clock::time_point start2=std::chrono::system_clock::now(); + + LCC3 lcc; + size_type corner_mark=lcc.get_new_mark(); + if(read_depending_extension(filename, lcc)==nullptr) + { + std::cout<<"[ERROR] problem when reading file "<<filename<<std::endl; + return false; + } + tetrahedralize_with_tetgen(lcc); + lcc.negate_mark(corner_mark); // At the beginning, all darts are corners + std::chrono::duration<double> diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Tetrahedral mesh computation: ", diff, "s."); + + start2=std::chrono::system_clock::now(); + + Pattern_substituer<LCC3> ps1; + ps1.load_fpatterns("data/tetra-to-hexa/fpattern/", + mark_fpattern_corners<LCC3>); + ps1.load_vpatterns("data/tetra-to-hexa/vpattern/", + mark_vpattern_corners<LCC3>); + + subdivide_edges(lcc); + subdivide_faces(lcc, corner_mark, ps1); + subdivide_volumes(lcc, corner_mark, ps1); + + diff=std::chrono::system_clock::now()-start2; + print_txt_with_endl(" Subdivision: ", diff, "s."); + + diff=std::chrono::system_clock::now()-start; + print_txt_with_endl("Create mesh TOTAL: ", diff, "s."); + std::cout<<"Final map: "; display_stats(lcc); + assert(lcc.is_valid()); + + if(draw) + { CGAL::draw(lcc); } + + if(save) + { + std::filesystem::path p(filename); + save_object_3D(p.stem().string()+"-hexa.mesh", lcc); + } + + lcc.free_mark(corner_mark); + return true; +} +//////////////////////////////////////////////////////////////////////////////// +int main(int argc, char** argv) +{ + if (argc<2) // We need at least one filename + { usage(argc, argv); } + + std::string filename; + bool draw; + bool save; + + process_command_line(argc, argv, + filename, + draw, + save); + + if (!create_mesh_from_off(filename, + draw, + save)) + { return EXIT_FAILURE; } + + return EXIT_SUCCESS; +} +////////////////////////////////////////////////////////////////////////////////