diff --git a/BGL/examples/BGL_OpenMesh/CMakeLists.txt b/BGL/examples/BGL_OpenMesh/CMakeLists.txt new file mode 100644 index 00000000000..02119ee3340 --- /dev/null +++ b/BGL/examples/BGL_OpenMesh/CMakeLists.txt @@ -0,0 +1,57 @@ +# Created by the script cgal_create_CMakeLists +# This is the CMake script for compiling a set of CGAL applications. + +project( BGL_OpenMesh ) + + +cmake_minimum_required(VERSION 2.8.11) + +# CGAL and its components +find_package( CGAL QUIET COMPONENTS ) + +if ( NOT CGAL_FOUND ) + + message(STATUS "This project requires the CGAL library, and will not be compiled.") + return() + +endif() + +# include helper file +include( ${CGAL_USE_FILE} ) + + +# Boost and its components +find_package( Boost REQUIRED ) + +if ( NOT Boost_FOUND ) + + message(STATUS "This project requires the Boost library, and will not be compiled.") + + return() + +endif() + +find_package( OpenMesh QUIET ) + +if ( OpenMesh_FOUND ) +include( UseOpenMesh ) +else() + message(STATUS "Examples that use OpenMesh will not be compiled.") +endif() + +# include for local directory + +# include for local package +include_directories( BEFORE ../../include ) + + +# Creating entries for all C++ files with "main" routine +# ########################################################## + +include( CGAL_CreateSingleSourceCGALProgram ) + +if(OpenMesh_FOUND) +create_single_source_cgal_program( "TriMesh.cpp" ) + target_link_libraries( TriMesh ${OPENMESH_LIBRARIES} ) +endif() + diff --git a/BGL/examples/BGL_OpenMesh/TriMesh.cpp b/BGL/examples/BGL_OpenMesh/TriMesh.cpp new file mode 100644 index 00000000000..c28e11f7886 --- /dev/null +++ b/BGL/examples/BGL_OpenMesh/TriMesh.cpp @@ -0,0 +1,51 @@ + +#include + +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; + +typedef OpenMesh::TriMesh_ArrayKernelT Mesh; + +typedef boost::graph_traits::vertex_descriptor vertex_descriptor; +typedef boost::graph_traits::face_descriptor face_descriptor; +typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + +int main(int argc, char** argv ) +{ + Mesh mesh; + + std::vector V; + V.push_back(add_vertex(mesh)); + V.push_back(add_vertex(mesh)); + V.push_back(add_vertex(mesh)); + add_face(V.begin(), V.end(), mesh); + + // OpenMesh::IO::read_mesh(mesh, (argc>1)?argv[1]:"in.off"); + + BOOST_FOREACH(vertex_descriptor vd, vertices(mesh)){ + BOOST_FOREACH(halfedge_descriptor hd, CGAL::halfedges_around_target(vd,mesh)){ + if(! CGAL::is_border(edge(hd,mesh),mesh)){ + CGAL::Euler::flip_edge(hd,mesh); + OpenMesh::IO::write_mesh(mesh, (argc>2)?argv[2]:"out.off"); + return 0; + } + } + } + return 0; +} diff --git a/BGL/examples/BGL_polyhedron_3/CMakeLists.txt b/BGL/examples/BGL_polyhedron_3/CMakeLists.txt new file mode 100644 index 00000000000..a84cf7493c0 --- /dev/null +++ b/BGL/examples/BGL_polyhedron_3/CMakeLists.txt @@ -0,0 +1,73 @@ +# Created by the script cgal_create_CMakeLists +# This is the CMake script for compiling a set of CGAL applications. + +project( BGL_polyhedron_3 ) + + +cmake_minimum_required(VERSION 2.8.11) + +# CGAL and its components +find_package( CGAL QUIET COMPONENTS ) + +if ( NOT CGAL_FOUND ) + + message(STATUS "This project requires the CGAL library, and will not be compiled.") + return() + +endif() + +# include helper file +include( ${CGAL_USE_FILE} ) + + +# Boost and its components +find_package( Boost REQUIRED ) + +if ( NOT Boost_FOUND ) + + message(STATUS "This project requires the Boost library, and will not be compiled.") + + return() + +endif() + +find_package( OpenMesh QUIET ) + +if ( OpenMesh_FOUND ) +include( UseOpenMesh ) +else() + message(STATUS "Examples that use OpenMesh will not be compiled.") +endif() + +# include for local directory + +# include for local package +include_directories( BEFORE ../../include ) + + +# Creating entries for all C++ files with "main" routine +# ########################################################## + +include( CGAL_CreateSingleSourceCGALProgram ) + +create_single_source_cgal_program( "distance.cpp" ) + +create_single_source_cgal_program( "incident_vertices.cpp" ) + +create_single_source_cgal_program( "kruskal.cpp" ) + +create_single_source_cgal_program( "kruskal_with_stored_id.cpp" ) + +create_single_source_cgal_program( "normals.cpp" ) + +create_single_source_cgal_program( "range.cpp" ) + +create_single_source_cgal_program( "transform_iterator.cpp" ) + + + +if(OpenMesh_FOUND) + create_single_source_cgal_program( "polyhedron_2_OpenMesh.cpp" ) + target_link_libraries( polyhedron_2_OpenMesh ${OPENMESH_LIBRARIES} ) +endif() + diff --git a/BGL/examples/BGL_polyhedron_3/polyhedron_2_OpenMesh.cpp b/BGL/examples/BGL_polyhedron_3/polyhedron_2_OpenMesh.cpp new file mode 100644 index 00000000000..55f64a8215d --- /dev/null +++ b/BGL/examples/BGL_polyhedron_3/polyhedron_2_OpenMesh.cpp @@ -0,0 +1,87 @@ + +#include + +#include +#include +#include + +#include + +#if 1 +#include +#include +#else +#include +#include +#endif + +#include + +#include + +#include +#include + + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::Vector_3 Vector; +typedef Kernel::Point_3 Point; +typedef CGAL::Polyhedron_3 Source; + +#if 1 +typedef OpenMesh::PolyMesh_ArrayKernelT Target; +#else +typedef OpenMesh::TriMesh_ArrayKernelT Target; +#endif +typedef boost::graph_traits::vertex_descriptor sm_vertex_descriptor; +typedef boost::graph_traits::vertex_descriptor tm_vertex_descriptor; + +typedef boost::graph_traits::halfedge_descriptor sm_halfedge_descriptor; +typedef boost::graph_traits::halfedge_descriptor tm_halfedge_descriptor; + +namespace OpenMesh { + +inline std::size_t hash_value(const VertexHandle& i) + { + return i.idx(); + } + +inline std::size_t hash_value(const HalfedgeHandle& i) + { + return i.idx(); + } + +inline std::size_t hash_value(const FaceHandle& i) + { + return i.idx(); + } + +} + +int main(int argc, char* argv[]) +{ + Source S; + Target T; + std::ifstream in((argc>1)?argv[1]:"cube.off"); + in >> S; + + { + boost::unordered_map v2v; + boost::unordered_map h2h; + + convert_surface_mesh(S,T,v2v,h2h); + OpenMesh::IO::write_mesh(T, "om.off"); + } + S.clear(); + { + boost::unordered_map v2v; + boost::unordered_map h2h; + + convert_surface_mesh(T,S,v2v,h2h); + std::ofstream out("reverse.off"); + out << S << std::endl; + } + + + return 0; +} diff --git a/BGL/include/CGAL/boost/graph/convert_surface_mesh.h b/BGL/include/CGAL/boost/graph/convert_surface_mesh.h new file mode 100644 index 00000000000..69457c6ad72 --- /dev/null +++ b/BGL/include/CGAL/boost/graph/convert_surface_mesh.h @@ -0,0 +1,82 @@ +// Copyright (c) 2015 GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org); you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 3 of the License, +// or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// +// Author(s) : Andreas Fabri + +#ifndef CGAL_BOOST_GRAPH_CONVERT_SURFACE_MESH_H +#define CGAL_BOOST_GRAPH_CONVERT_SURFACE_MESH_H + +#include +#include +#include +#include + +namespace CGAL { + + template + void convert_surface_mesh(const SourceMesh& sm, TargetMesh& tm, V2V& v2v, H2H& h2h) +{ + typedef typename boost::graph_traits::vertex_descriptor sm_vertex_descriptor; + typedef typename boost::graph_traits::vertex_descriptor tm_vertex_descriptor; + + typedef typename boost::graph_traits::face_descriptor sm_face_descriptor; + typedef typename boost::graph_traits::face_descriptor tm_face_descriptor; + + typedef typename boost::graph_traits::halfedge_descriptor sm_halfedge_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor tm_halfedge_descriptor; + + typedef typename boost::property_map::const_type sm_PMap; + typedef typename boost::property_map::type tm_PMap; + + sm_PMap sm_pmap = get(vertex_point, sm); + tm_PMap tm_pmap = get(vertex_point, tm); + + + BOOST_FOREACH(sm_vertex_descriptor svd, vertices(sm)){ + tm_vertex_descriptor tvd = add_vertex(tm); + v2v.insert(std::make_pair(svd, tvd)); + put(tm_pmap, tvd, get(sm_pmap, svd)); + } + + boost::unordered_map f2f; + BOOST_FOREACH(sm_face_descriptor sfd, faces(sm)){ + std::vector tv; + BOOST_FOREACH(sm_vertex_descriptor svd, vertices_around_face(halfedge(sfd,sm),sm)){ + tv.push_back(v2v.at(svd)); + } + f2f[sfd] = Euler::add_face(tv,tm); + } + + BOOST_FOREACH(sm_face_descriptor sfd, faces(sm)){ + sm_halfedge_descriptor shd = halfedge(sfd,sm), done(shd); + tm_halfedge_descriptor thd = halfedge(f2f[sfd],tm); + tm_vertex_descriptor tvd = v2v.at(target(shd,sm)); + while(target(thd,tm) != tvd){ + thd = next(thd,tm); + } + do { + h2h.insert(std::make_pair(shd, thd)); + shd = next(shd,sm); + thd = next(thd,tm); + }while(shd != done); + } + +} + +} // namespace CGAL + +#endif // CGAL_BOOST_GRAPH_CONVERT_SURFACE_MESH_H diff --git a/BGL/include/CGAL/boost/graph/graph_traits_PolyMesh_ArrayKernelT.h b/BGL/include/CGAL/boost/graph/graph_traits_PolyMesh_ArrayKernelT.h index 05207f83f7a..7742bc729e8 100644 --- a/BGL/include/CGAL/boost/graph/graph_traits_PolyMesh_ArrayKernelT.h +++ b/BGL/include/CGAL/boost/graph/graph_traits_PolyMesh_ArrayKernelT.h @@ -358,6 +358,9 @@ typename boost::graph_traits >::halfedge_desc halfedge(typename boost::graph_traits >::vertex_descriptor v, const OpenMesh::PolyMesh_ArrayKernelT& sm) { + if(sm.halfedge_handle(v) == boost::graph_traits >::null_halfedge()){ + return boost::graph_traits >::null_halfedge(); + } // prev because OpenMesh stores out-going halfedges // return sm.prev_halfedge_handle(sm.halfedge_handle(v)); return sm.opposite_halfedge_handle(sm.halfedge_handle(v)); @@ -615,7 +618,7 @@ remove_face(typename boost::graph_traits >::f sm.status(f).set_deleted(true); } - +#if 0 // conflits with function in Euler_operations.h template std::pair >::edge_descriptor, bool> @@ -625,6 +628,7 @@ add_edge(typename boost::graph_traits >::vert return sm.new_edge(v1, v2); } +#endif template typename boost::graph_traits >::face_descriptor diff --git a/BGL/include/CGAL/boost/graph/graph_traits_Surface_mesh.h b/BGL/include/CGAL/boost/graph/graph_traits_Surface_mesh.h index f55c8a7d4ce..8e2a5986e9f 100644 --- a/BGL/include/CGAL/boost/graph/graph_traits_Surface_mesh.h +++ b/BGL/include/CGAL/boost/graph/graph_traits_Surface_mesh.h @@ -515,7 +515,7 @@ add_face(InputIterator begin, InputIterator end, CGAL::Surface_mesh

& sm) } template -bool is_valid(CGAL::Surface_mesh

& sm, bool verbose = false) +bool is_valid(const CGAL::Surface_mesh

& sm, bool verbose = false) { return sm.is_valid(verbose); } diff --git a/BGL/include/CGAL/boost/graph/graph_traits_TriMesh_ArrayKernelT.h b/BGL/include/CGAL/boost/graph/graph_traits_TriMesh_ArrayKernelT.h new file mode 100644 index 00000000000..53e6a5754f4 --- /dev/null +++ b/BGL/include/CGAL/boost/graph/graph_traits_TriMesh_ArrayKernelT.h @@ -0,0 +1,614 @@ +// Copyright (c) 2007 GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org); you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 3 of the License, +// or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// +// Author(s) : Andreas Fabri, Philipp Moeller + +#ifndef CGAL_BOOST_GRAPH_GRAPH_TRAITS_TRIMESH_ARRAYKERNELT_H +#define CGAL_BOOST_GRAPH_GRAPH_TRAITS_TRIMESH_ARRAYKERNELT_H + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +// http://openmesh.org/Documentation/OpenMesh-Doc-Latest/classOpenMesh_1_1Concepts_1_1KernelT.html + +#if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4267) +#endif + +namespace boost { + +template +struct graph_traits< OpenMesh::TriMesh_ArrayKernelT > +{ +private: + typedef OpenMesh::TriMesh_ArrayKernelT SM; + + struct SM_graph_traversal_category : public virtual boost::bidirectional_graph_tag, + public virtual boost::vertex_list_graph_tag, + public virtual boost::edge_list_graph_tag + {}; + +public: + // Graph + typedef typename SM::VertexHandle vertex_descriptor; + typedef typename SM::Point vertex_property_type; + typedef typename CGAL::internal::OMesh_edge edge_descriptor; + typedef boost::undirected_tag directed_category; + typedef boost::disallow_parallel_edge_tag edge_parallel_category; + typedef SM_graph_traversal_category traversal_category; + + // HalfedgeGraph + typedef typename SM::HalfedgeHandle halfedge_descriptor; + + // FaceGraph + typedef typename SM::FaceHandle face_descriptor; + + // VertexListGraph + typedef typename SM::VertexIter vertex_iterator; + typedef unsigned int vertices_size_type; + // EdgeListGraph + typedef boost::transform_iterator< + CGAL::internal::Convert_omesh_edge, + typename SM::EdgeIter, + edge_descriptor> edge_iterator; + + typedef unsigned int edges_size_type; + // HalfEdgeListGraph + typedef typename SM::HalfedgeIter halfedge_iterator; + typedef unsigned int halfedges_size_type; + // FaceListGraph + typedef typename SM::FaceIter face_iterator; + typedef unsigned int faces_size_type; + + // IncidenceGraph + typedef unsigned int degree_size_type; + + + typedef CGAL::In_edge_iterator in_edge_iterator; + + typedef CGAL::Out_edge_iterator out_edge_iterator; + + // nulls + static vertex_descriptor null_vertex() { return vertex_descriptor(); } + static face_descriptor null_face() { return face_descriptor(); } + static halfedge_descriptor null_halfedge() { return halfedge_descriptor(); } +}; + +template +struct graph_traits< const OpenMesh::TriMesh_ArrayKernelT > + : public graph_traits< OpenMesh::TriMesh_ArrayKernelT > +{ }; + +} // namespace boost + +namespace OpenMesh { + +template +typename boost::graph_traits >::vertices_size_type +num_vertices(const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.n_vertices(); +} + + +template +typename boost::graph_traits >::edges_size_type +num_edges(const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.n_edges(); +} + + +template +typename boost::graph_traits >::degree_size_type +degree(typename boost::graph_traits >::vertex_descriptor v, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.valence(v); +} + + +template +typename boost::graph_traits >::degree_size_type +out_degree(typename boost::graph_traits >::vertex_descriptor v, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.valence(v); +} + + +template +typename boost::graph_traits >::degree_size_type +in_degree(typename boost::graph_traits >::vertex_descriptor v, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.valence(v); +} + + +template +typename boost::graph_traits >::vertex_descriptor +source(typename boost::graph_traits >::edge_descriptor e, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.from_vertex_handle(e.halfedge()); +} + +template +typename boost::graph_traits >::vertex_descriptor +source(typename boost::graph_traits >::halfedge_descriptor h, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.from_vertex_handle(h); +} + + +template +typename boost::graph_traits >::vertex_descriptor +target(typename boost::graph_traits >::edge_descriptor e, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.to_vertex_handle(e.halfedge()); +} + +template +typename boost::graph_traits >::vertex_descriptor +target(typename boost::graph_traits >::halfedge_descriptor h, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.to_vertex_handle(h); +} + +template +CGAL::Iterator_range >::vertex_iterator> +vertices(const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return CGAL::make_range(sm.vertices_sbegin(), sm.vertices_end()); +} + + +template +CGAL::Iterator_range >::edge_iterator> +edges(const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + typedef typename boost::graph_traits >::edge_iterator iterator; + iterator beg(sm.edges_sbegin()); + iterator end(sm.edges_end()); + return CGAL::make_range(beg,end); +} + + +template +CGAL::Iterator_range >::in_edge_iterator> +in_edges(typename boost::graph_traits >::vertex_descriptor v, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + typedef typename boost::graph_traits >::in_edge_iterator Iter; + + return CGAL::make_range(Iter(halfedge(v,sm),sm), Iter(halfedge(v,sm),sm,1)); +} + + +template +CGAL::Iterator_range >::out_edge_iterator> +out_edges(typename boost::graph_traits >::vertex_descriptor v, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + typedef typename boost::graph_traits >::out_edge_iterator Iter; + return CGAL::make_range(Iter(halfedge(v,sm),sm), Iter(halfedge(v,sm),sm,1)); +} + + +template +std::pair >::edge_descriptor, + bool> +edge(typename boost::graph_traits >::vertex_descriptor u, + typename boost::graph_traits >::vertex_descriptor v, + const OpenMesh::TriMesh_ArrayKernelT& sm) { + typename boost::graph_traits >::edge_descriptor + he(sm.find_halfedge(u, v)); + return std::make_pair(he, he.is_valid()); +} + + +// +// HalfedgeGraph +// +template +typename boost::graph_traits >::halfedge_descriptor +next(typename boost::graph_traits >::halfedge_descriptor h, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.next_halfedge_handle(h); +} + +template +typename boost::graph_traits >::halfedge_descriptor +prev(typename boost::graph_traits >::halfedge_descriptor h, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.prev_halfedge_handle(h); +} + +template +typename boost::graph_traits >::halfedge_descriptor +opposite(typename boost::graph_traits >::halfedge_descriptor h, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.opposite_halfedge_handle(h); +} + +template +typename boost::graph_traits >::edge_descriptor +edge(typename boost::graph_traits >::halfedge_descriptor h, + const OpenMesh::TriMesh_ArrayKernelT& /*sm*/) +{ + return typename boost::graph_traits >::edge_descriptor(h); +} + +template +typename boost::graph_traits >::halfedge_descriptor +halfedge(typename boost::graph_traits >::edge_descriptor e, + const OpenMesh::TriMesh_ArrayKernelT&) +{ + return e.halfedge(); +} + +template +typename boost::graph_traits >::halfedge_descriptor +halfedge(typename boost::graph_traits >::vertex_descriptor v, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + if(sm.halfedge_handle(v) == boost::graph_traits >::null_halfedge()){ + return boost::graph_traits >::null_halfedge(); + } + // prev because OpenMesh stores out-going halfedges + // return sm.prev_halfedge_handle(sm.halfedge_handle(v)); + return sm.opposite_halfedge_handle(sm.halfedge_handle(v)); +} + + +template +std::pair< + typename boost::graph_traits >::halfedge_descriptor, + bool +> +halfedge(typename boost::graph_traits >::vertex_descriptor u, + typename boost::graph_traits >::vertex_descriptor v, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + typename boost::graph_traits >::halfedge_descriptor h = sm.find_halfedge(u, v); + return std::make_pair(h, h.is_valid()); +} + + + +// +// HalfedgeListGraph +// +template +CGAL::Iterator_range >::halfedge_iterator> +halfedges(const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return CGAL::make_range(sm.halfedges_sbegin(), sm.halfedges_end()); +} + +template +typename boost::graph_traits >::halfedges_size_type +num_halfedges(const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.n_halfedges(); +} + + + +// +// MutableHalfedgeGraph +// +template +void +set_next(typename boost::graph_traits >::halfedge_descriptor h1, + typename boost::graph_traits >::halfedge_descriptor h2, + OpenMesh::TriMesh_ArrayKernelT& sm) +{ + sm.set_next_halfedge_handle(h1, h2); +} + + + +template +void +set_target(typename boost::graph_traits >::halfedge_descriptor h, + typename boost::graph_traits >::vertex_descriptor v, + OpenMesh::TriMesh_ArrayKernelT& sm) +{ + sm.set_vertex_handle(h, v); +} + + +template +void +set_halfedge(typename boost::graph_traits >::vertex_descriptor v, + typename boost::graph_traits >::halfedge_descriptor h, + OpenMesh::TriMesh_ArrayKernelT& sm) +{ + sm.set_halfedge_handle(v, sm.opposite_halfedge_handle(h)); +} + + +template +void +adjust_border_halfedge(typename boost::graph_traits >::vertex_descriptor v, + OpenMesh::TriMesh_ArrayKernelT& sm) +{ + sm.adjust_outgoing_halfedge(v); +} + +template +void +garbage_collection(OpenMesh::TriMesh_ArrayKernelT& sm) +{ + sm.garbage_collection(); +} + +template +typename boost::graph_traits >::edge_descriptor +add_edge(OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return edge(sm.new_edge(boost::graph_traits >::null_vertex(), + boost::graph_traits >::null_vertex() ), sm); +} + + +// +// FaceGraph +// +template +typename boost::graph_traits >::halfedge_descriptor +halfedge(typename boost::graph_traits >::face_descriptor f, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.halfedge_handle(f); +} + +template +typename boost::graph_traits >::face_descriptor +face(typename boost::graph_traits >::halfedge_descriptor h, + const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.face_handle(h); +} + + + +// +// MutableFaceGraph +// +template +void +set_face(typename boost::graph_traits >::halfedge_descriptor h, + typename boost::graph_traits >::face_descriptor f, + OpenMesh::TriMesh_ArrayKernelT& sm) +{ + sm.set_face_handle(h, f); +} + + +template +void +set_halfedge(typename boost::graph_traits >::face_descriptor f, + typename boost::graph_traits >::halfedge_descriptor h, + OpenMesh::TriMesh_ArrayKernelT& sm) +{ + sm.set_halfedge_handle(f, h); +} + + +// +// FaceListGraph +// +template +typename boost::graph_traits >::faces_size_type +num_faces(const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.n_faces(); +} + +template +CGAL::Iterator_range >::face_iterator> +faces(const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return CGAL::make_range(sm.faces_sbegin(), sm.faces_end()); +} + + +template +typename boost::graph_traits >::vertex_descriptor +add_vertex(OpenMesh::TriMesh_ArrayKernelT& sm) { + return sm.new_vertex(); +} + + /* + +// MutableGraph +// add a vertex with a default constructed property +template +typename boost::graph_traits >::vertex_descriptor +add_vertex(OpenMesh::TriMesh_ArrayKernelT& sm) { + return sm.add_vertex(typename boost::graph_traits >::vertex_property_type()); +} + +template +typename boost::graph_traits >::vertex_descriptor +add_vertex(const typename boost::graph_traits >::vertex_property_type& p, OpenMesh::TriMesh_ArrayKernelT& sm) { + return sm.add_vertex(p); +} + +template +void +clear_vertex(typename boost::graph_traits >::vertex_descriptor, + OpenMesh::TriMesh_ArrayKernelT&) { + CGAL_assert(false); +} + + */ + +template +void +remove_vertex(typename boost::graph_traits >::vertex_descriptor v, + OpenMesh::TriMesh_ArrayKernelT& sm) { + + sm.request_face_status(); + sm.request_vertex_status(); + sm.request_halfedge_status(); + sm.set_halfedge_handle(v, typename boost::graph_traits >::halfedge_descriptor()); + sm.status(v).set_deleted(true); +} + + +template +void +remove_edge(typename boost::graph_traits >::vertex_descriptor u, + typename boost::graph_traits >::vertex_descriptor v, + OpenMesh::TriMesh_ArrayKernelT& sm) +{ + typename boost::graph_traits >::edge_descriptor e = edge(u, v, sm); + remove_edge(e,sm); +} + +template +void +remove_edge(typename boost::graph_traits >::edge_descriptor e, + OpenMesh::TriMesh_ArrayKernelT& sm) +{ + sm.request_face_status(); + sm.request_vertex_status(); + sm.request_halfedge_status(); + sm.request_edge_status(); + + typedef typename boost::graph_traits >::halfedge_descriptor halfedge_descriptor; + + halfedge_descriptor h1 = halfedge(e,sm); + halfedge_descriptor h2 = opposite(halfedge(e,sm),sm); + sm.status(sm.edge_handle(h1)).set_deleted(true); + sm.status(h1).set_deleted(true); + sm.status(h2).set_deleted(true); + +} + + +template +void +remove_edge(typename boost::graph_traits >::edge_iterator eiter, + OpenMesh::TriMesh_ArrayKernelT& sm) +{ + remove_edge(*eiter, sm); +} + +template +void +remove_face(typename boost::graph_traits >::face_descriptor f, + OpenMesh::TriMesh_ArrayKernelT& sm) +{ + + sm.request_face_status(); + sm.request_vertex_status(); + sm.request_halfedge_status(); + + set_halfedge(f, typename boost::graph_traits >::halfedge_descriptor(), sm); + sm.status(f).set_deleted(true); +} + +#if 0 // conflits with function in Euler_operations.h +template +std::pair >::edge_descriptor, + bool> +add_edge(typename boost::graph_traits >::vertex_descriptor v1, + typename boost::graph_traits >::vertex_descriptor v2, + OpenMesh::TriMesh_ArrayKernelT& sm) { + + return sm.new_edge(v1, v2); +} +#endif + +template +typename boost::graph_traits >::face_descriptor +add_face(OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return sm.new_face(); +} + +template +typename boost::graph_traits >::face_descriptor +add_face(InputIterator begin, InputIterator end, OpenMesh::TriMesh_ArrayKernelT& sm) +{ + typedef typename boost::graph_traits >::vertex_descriptor vertex_descriptor; + assert(begin!= end); + vertex_descriptor u = *begin; + ++begin; + assert(begin!= end); + vertex_descriptor v = *begin; + ++begin; + assert(begin!= end); + vertex_descriptor w = *begin; + + return sm.add_face(u,v,w); +} + +template +bool is_valid(OpenMesh::TriMesh_ArrayKernelT& sm, bool /* verbose */ = false) +{ + return CGAL::is_valid_polygon_mesh(sm); +} + +} // namespace OpenMesh + + +#ifndef CGAL_NO_DEPRECATED_CODE +#include + +namespace boost { + // The following functions were defined in the namespace boost + using OpenMesh::vertices; + using OpenMesh::edges; + using OpenMesh::num_vertices; + using OpenMesh::num_edges; + using OpenMesh::out_edges; + using OpenMesh::in_edges; + using OpenMesh::target; + using OpenMesh::source; +} // namespace boost +#endif //CGAL_NO_DEPRECATED_CODE + +#if defined(BOOST_MSVC) +# pragma warning(pop) +#endif + +#endif // CGAL_BOOST_GRAPH_TRAITS_TRIMESH_ARRAYKERNELT_H diff --git a/BGL/include/CGAL/boost/graph/internal/helpers.h b/BGL/include/CGAL/boost/graph/internal/helpers.h index 154f2116e76..aeebc9347b7 100644 --- a/BGL/include/CGAL/boost/graph/internal/helpers.h +++ b/BGL/include/CGAL/boost/graph/internal/helpers.h @@ -196,7 +196,7 @@ adjust_incoming_halfedge(typename boost::graph_traits::vertex_descriptor } do { - if(is_border(h, g)) + if(face(h, g)==boost::graph_traits::null_face()) { set_halfedge(v, h, g); return; diff --git a/BGL/include/CGAL/boost/graph/properties_PolyMesh_ArrayKernelT.h b/BGL/include/CGAL/boost/graph/properties_PolyMesh_ArrayKernelT.h index d5c092e638a..d5a8073d3f1 100644 --- a/BGL/include/CGAL/boost/graph/properties_PolyMesh_ArrayKernelT.h +++ b/BGL/include/CGAL/boost/graph/properties_PolyMesh_ArrayKernelT.h @@ -85,17 +85,17 @@ public: }; -template +template class OM_edge_weight_pmap - : public boost::put_get_helper::Scalar , OM_edge_weight_pmap > + : public boost::put_get_helper > { public: typedef boost::readable_property_map_tag category; - typedef typename OpenMesh::PolyMesh_ArrayKernelT::Scalar value_type; + typedef typename OpenMesh::Scalar value_type; typedef value_type reference; - typedef typename boost::graph_traits >::edge_descriptor key_type; + typedef typename boost::graph_traits::edge_descriptor key_type; - OM_edge_weight_pmap(const OpenMesh::PolyMesh_ArrayKernelT& sm) + OM_edge_weight_pmap(const OpenMesh& sm) : sm_(sm) {} @@ -105,7 +105,7 @@ public: } private: - const OpenMesh::PolyMesh_ArrayKernelT& sm_; + const OpenMesh& sm_; }; template @@ -124,25 +124,25 @@ public: }; - template -class OM_point_pmap //: public boost::put_get_helper > +template +class OM_point_pmap //: public boost::put_get_helper > { public: typedef boost::read_write_property_map_tag category; #if defined(CGAL_USE_OM_POINTS) - typedef typename OpenMesh::PolyMesh_ArrayKernelT::Point value_type; - typedef const typename OpenMesh::PolyMesh_ArrayKernelT::Point& reference; + typedef typename OpenMesh::Point value_type; + typedef const typename OpenMesh::Point& reference; #else typedef P value_type; typedef P reference; #endif - typedef typename boost::graph_traits< OpenMesh::PolyMesh_ArrayKernelT >::vertex_descriptor key_type; + typedef typename boost::graph_traits::vertex_descriptor key_type; OM_point_pmap() : sm_(NULL) {} - OM_point_pmap(const OpenMesh::PolyMesh_ArrayKernelT& sm) + OM_point_pmap(const OpenMesh& sm) : sm_(&sm) {} @@ -156,36 +156,36 @@ public: return sm_->point(v); #else CGAL_assertion(sm_!=NULL); - typename OpenMesh::PolyMesh_ArrayKernelT::Point const& omp = sm_->point(v); + typename OpenMesh::Point const& omp = sm_->point(v); return value_type(omp[0], omp[1], omp[2]); #endif } - inline friend reference get(const OM_point_pmap& pm, key_type v) + inline friend reference get(const OM_point_pmap& pm, key_type v) { CGAL_precondition(pm.sm_!=NULL); #if defined(CGAL_USE_OM_POINTS) return pm.sm_->point(v); #else CGAL_assertion(pm.sm_!=NULL); - typename OpenMesh::PolyMesh_ArrayKernelT::Point const& omp = pm.sm_->point(v); + typename OpenMesh::Point const& omp = pm.sm_->point(v); return value_type(omp[0], omp[1], omp[2]); #endif } - inline friend void put(const OM_point_pmap& pm, key_type v, const value_type& p) + inline friend void put(const OM_point_pmap& pm, key_type v, const value_type& p) { CGAL_precondition(pm.sm_!=NULL); #if defined(CGAL_USE_OM_POINTS) - const_cast&>(*pm.sm_).set_point(v,p); + const_cast(*pm.sm_).set_point(v,p); #else - const_cast&>(*pm.sm_).set_point - (v, typename OpenMesh::PolyMesh_ArrayKernelT::Point((float)p[0], (float)p[1], (float)p[2])); + const_cast(*pm.sm_).set_point + (v, typename OpenMesh::Point((float)p[0], (float)p[1], (float)p[2])); #endif } private: - const OpenMesh::PolyMesh_ArrayKernelT* sm_; + const OpenMesh* sm_; }; @@ -202,8 +202,9 @@ namespace boost { template struct property_map, boost::edge_weight_t > { - typedef CGAL::OM_edge_weight_pmap type; - typedef CGAL::OM_edge_weight_pmap const_type; + typedef OpenMesh::PolyMesh_ArrayKernelT Mesh; + typedef CGAL::OM_edge_weight_pmap type; + typedef CGAL::OM_edge_weight_pmap const_type; }; @@ -215,8 +216,9 @@ struct property_map, boost::edge_weight_t > template struct property_map, boost::vertex_index_t > { - typedef CGAL::OM_index_pmap >::vertex_descriptor> type; - typedef CGAL::OM_index_pmap >::vertex_descriptor> const_type; + typedef OpenMesh::PolyMesh_ArrayKernelT Mesh; + typedef CGAL::OM_index_pmap::vertex_descriptor> type; + typedef CGAL::OM_index_pmap::vertex_descriptor> const_type; }; @@ -227,8 +229,9 @@ struct property_map, boost::vertex_index_t > template struct property_map, boost::face_index_t > { - typedef CGAL::OM_index_pmap >::face_descriptor> type; - typedef CGAL::OM_index_pmap >::face_descriptor> const_type; + typedef OpenMesh::PolyMesh_ArrayKernelT Mesh; + typedef CGAL::OM_index_pmap::face_descriptor> type; + typedef CGAL::OM_index_pmap::face_descriptor> const_type; }; // @@ -238,8 +241,9 @@ struct property_map, boost::face_index_t > template struct property_map, boost::edge_index_t > { - typedef CGAL::OM_index_pmap >::edge_descriptor> type; - typedef CGAL::OM_index_pmap >::edge_descriptor> const_type; + typedef OpenMesh::PolyMesh_ArrayKernelT Mesh; + typedef CGAL::OM_index_pmap::edge_descriptor> type; + typedef CGAL::OM_index_pmap::edge_descriptor> const_type; }; // @@ -249,8 +253,9 @@ struct property_map, boost::edge_index_t > template struct property_map, boost::halfedge_index_t > { - typedef CGAL::OM_index_pmap >::halfedge_descriptor> type; - typedef CGAL::OM_index_pmap >::halfedge_descriptor> const_type; + typedef OpenMesh::PolyMesh_ArrayKernelT Mesh; + typedef CGAL::OM_index_pmap::halfedge_descriptor> type; + typedef CGAL::OM_index_pmap::halfedge_descriptor> const_type; }; @@ -258,7 +263,8 @@ template struct property_map, boost::vertex_point_t > { typedef CGAL::Exact_predicates_inexact_constructions_kernel::Point_3 P; - typedef CGAL::OM_point_pmap type; + typedef OpenMesh::PolyMesh_ArrayKernelT Mesh; + typedef CGAL::OM_point_pmap type; typedef type const_type; }; @@ -271,7 +277,8 @@ template typename boost::property_map, boost::edge_weight_t>::const_type get(boost::edge_weight_t, const OpenMesh::PolyMesh_ArrayKernelT& sm) { - return CGAL::OM_edge_weight_pmap(sm); + typedef OpenMesh::PolyMesh_ArrayKernelT Mesh; + return CGAL::OM_edge_weight_pmap(sm); } template @@ -279,7 +286,8 @@ typename OpenMesh::PolyMesh_ArrayKernelT::Scalar get(boost::edge_weight_t, const OpenMesh::PolyMesh_ArrayKernelT& sm, const typename boost::graph_traits >::edge_descriptor& e) { - return CGAL::OM_edge_weight_pmap(sm)[e]; + typedef OpenMesh::PolyMesh_ArrayKernelT Mesh; + return CGAL::OM_edge_weight_pmap(sm)[e]; } @@ -287,14 +295,16 @@ template CGAL::OM_index_pmap >::vertex_descriptor> get(const boost::vertex_index_t&, const OpenMesh::PolyMesh_ArrayKernelT&) { - return CGAL::OM_index_pmap >::vertex_descriptor>(); + typedef OpenMesh::PolyMesh_ArrayKernelT Mesh; + return CGAL::OM_index_pmap::vertex_descriptor>(); } template typename boost::property_map, boost::face_index_t>::const_type get(const boost::face_index_t&, const OpenMesh::PolyMesh_ArrayKernelT&) { - return CGAL::OM_index_pmap >::face_descriptor>(); + typedef OpenMesh::PolyMesh_ArrayKernelT Mesh; + return CGAL::OM_index_pmap::face_descriptor>(); } template @@ -312,11 +322,12 @@ get(const boost::halfedge_index_t&, const OpenMesh::PolyMesh_ArrayKernelT&) } template -CGAL::OM_point_pmap +CGAL::OM_point_pmap, + typename CGAL::Exact_predicates_inexact_constructions_kernel::Point_3> get(boost::vertex_point_t, const OpenMesh::PolyMesh_ArrayKernelT& g) { typedef typename CGAL::Exact_predicates_inexact_constructions_kernel::Point_3 P; - return CGAL::OM_point_pmap(g); + return CGAL::OM_point_pmap, P>(g); } // get for intrinsic properties diff --git a/BGL/include/CGAL/boost/graph/properties_TriMesh_ArrayKernelT.h b/BGL/include/CGAL/boost/graph/properties_TriMesh_ArrayKernelT.h new file mode 100644 index 00000000000..f19093c3670 --- /dev/null +++ b/BGL/include/CGAL/boost/graph/properties_TriMesh_ArrayKernelT.h @@ -0,0 +1,195 @@ +// Copyright (c) 2014 GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org); you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 3 of the License, +// or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// +// Author(s) : Philipp Möller + + +#ifndef CGAL_PROPERTIES_TRIMESH_ARRAYKERNELT_H +#define CGAL_PROPERTIES_TRIMESH_ARRAYKERNELT_H + +#include +#include +#include +#include +#include +#include + + +// overloads and specializations in the boost namespace +namespace boost { + +// +// edge_weight +// +template +struct property_map, boost::edge_weight_t > +{ + typedef OpenMesh::TriMesh_ArrayKernelT Mesh; + typedef CGAL::OM_edge_weight_pmap type; + typedef CGAL::OM_edge_weight_pmap const_type; +}; + + + +// +// vertex_index +// +template +struct property_map, boost::vertex_index_t > +{ + typedef CGAL::OM_index_pmap >::vertex_descriptor> type; + typedef CGAL::OM_index_pmap >::vertex_descriptor> const_type; +}; + + +// +// face_index +// + +template +struct property_map, boost::face_index_t > +{ + typedef CGAL::OM_index_pmap >::face_descriptor> type; + typedef CGAL::OM_index_pmap >::face_descriptor> const_type; +}; + +// +// edge_index +// + +template +struct property_map, boost::edge_index_t > +{ + typedef CGAL::OM_index_pmap >::edge_descriptor> type; + typedef CGAL::OM_index_pmap >::edge_descriptor> const_type; +}; + +// +// halfedge_index +// + +template +struct property_map, boost::halfedge_index_t > +{ + typedef CGAL::OM_index_pmap >::halfedge_descriptor> type; + typedef CGAL::OM_index_pmap >::halfedge_descriptor> const_type; +}; + + +template +struct property_map, boost::vertex_point_t > +{ + typedef CGAL::Exact_predicates_inexact_constructions_kernel::Point_3 P; + typedef OpenMesh::TriMesh_ArrayKernelT Mesh; + typedef CGAL::OM_point_pmap type; + typedef type const_type; +}; + +} // namespace boost + +namespace boost { + + +template +typename boost::property_map, boost::edge_weight_t>::const_type +get(boost::edge_weight_t, const OpenMesh::TriMesh_ArrayKernelT& sm) +{ + return CGAL::OM_edge_weight_pmap(sm); +} + +template +typename OpenMesh::TriMesh_ArrayKernelT::Scalar +get(boost::edge_weight_t, const OpenMesh::TriMesh_ArrayKernelT& sm, + const typename boost::graph_traits >::edge_descriptor& e) +{ + return CGAL::OM_edge_weight_pmap(sm)[e]; +} + + +template +CGAL::OM_index_pmap >::vertex_descriptor> +get(const boost::vertex_index_t&, const OpenMesh::TriMesh_ArrayKernelT&) +{ + return CGAL::OM_index_pmap >::vertex_descriptor>(); +} + +template +typename boost::property_map, boost::face_index_t>::const_type +get(const boost::face_index_t&, const OpenMesh::TriMesh_ArrayKernelT&) +{ + return CGAL::OM_index_pmap >::face_descriptor>(); +} + +template +CGAL::OM_index_pmap >::edge_descriptor> +get(const boost::edge_index_t&, const OpenMesh::TriMesh_ArrayKernelT&) +{ + return CGAL::OM_index_pmap >::edge_descriptor>(); +} + +template +CGAL::OM_index_pmap >::halfedge_descriptor> +get(const boost::halfedge_index_t&, const OpenMesh::TriMesh_ArrayKernelT&) +{ + return CGAL::OM_index_pmap >::halfedge_descriptor>(); +} + +template +CGAL::OM_point_pmap, + typename CGAL::Exact_predicates_inexact_constructions_kernel::Point_3> +get(boost::vertex_point_t, const OpenMesh::TriMesh_ArrayKernelT& g) +{ + typedef typename CGAL::Exact_predicates_inexact_constructions_kernel::Point_3 P; + typedef OpenMesh::TriMesh_ArrayKernelT Mesh; + return CGAL::OM_point_pmap(g); +} + +// get for intrinsic properties +#define CGAL_OM_INTRINSIC_PROPERTY(RET, PROP, TYPE) \ + template \ + RET \ + get(PROP p, const OpenMesh::TriMesh_ArrayKernelT& sm, \ + typename boost::graph_traits< OpenMesh::TriMesh_ArrayKernelT >::TYPE x) \ + { return get(get(p, sm), x); } \ + + CGAL_OM_INTRINSIC_PROPERTY(int, boost::vertex_index_t, vertex_descriptor) + CGAL_OM_INTRINSIC_PROPERTY(int, boost::edge_index_t, edge_descriptor) + CGAL_OM_INTRINSIC_PROPERTY(int, boost::halfedge_index_t, halfedge_descriptor) + CGAL_OM_INTRINSIC_PROPERTY(int, boost::face_index_t, face_descriptor) + // CGAL_OM_INTRINSIC_PROPERTY(std::size_t, boost::halfedge_index_t, face_descriptor) + CGAL_OM_INTRINSIC_PROPERTY(typename CGAL::Exact_predicates_inexact_constructions_kernel::Point_3, boost::vertex_point_t, vertex_descriptor) + +#undef CGAL_OM_INTRINSIC_PROPERTY + +// put for intrinsic properties +// only available for vertex_point + +template +void +put(boost::vertex_point_t p, OpenMesh::TriMesh_ArrayKernelT& g, + typename boost::graph_traits< OpenMesh::TriMesh_ArrayKernelT >::vertex_descriptor vd, + const typename K::Point& point) +{ + put(get(p,g), vd, point); +} + + +} // namespace boost + + + +#endif /* CGAL_PROPERTIES_TRIMESH_ARRAYKERNELT_H */ diff --git a/BGL/include/CGAL/boost/graph/visitor.h b/BGL/include/CGAL/boost/graph/visitor.h new file mode 100644 index 00000000000..7b7b8e8926f --- /dev/null +++ b/BGL/include/CGAL/boost/graph/visitor.h @@ -0,0 +1,694 @@ +// Copyright (c) 2014 GeometryFactory (France). All rights reserved. +// +// This file is part of CGAL (www.cgal.org); you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 3 of the License, +// or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// +// Author(s) : Jane Tournois + +#ifndef CGAL_BOOST_GRAPH_VISITOR_H +#define CGAL_BOOST_GRAPH_VISITOR_H + +#include +#include +#include + +namespace boost +{ + template + struct graph_traits< + boost::tuple, boost::reference_wrapper > > + : boost::graph_traits< Graph > + { + typedef boost::graph_traits Base; + typedef typename Base::vertex_property_type vertex_property_type; + }; + + template + struct graph_traits< + boost::tuple, boost::reference_wrapper > const > + : boost::graph_traits< Graph > + {}; + + template + struct property_map< + boost::tuple, boost::reference_wrapper >, + PropertyTag> + : public property_map + {}; + + template + struct property_map< + const boost::tuple, boost::reference_wrapper >, + PropertyTag> + : public property_map + {}; + +} // namespace boost + +namespace CGAL +{ +template +boost::tuple, + boost::reference_wrapper > +make_graph_with_visitor(V& v, Graph& g) +{ + return boost::make_tuple(boost::ref(v), boost::ref(g)); +} + +template +class Visitor_base +{ +public: + typedef typename boost::graph_traits gt; + typedef typename gt::halfedge_descriptor halfedge_descriptor; + typedef typename gt::edge_descriptor edge_descriptor; + typedef typename gt::vertex_descriptor vertex_descriptor; + + +}; + + +//// OVERLOADS FOR Visitor + +template +void num_vertices(Visitor_base& w) +{} +template +void num_edges(Visitor_base& w) +{} +template +void degree(typename boost::graph_traits::vertex_descriptor v + , const Visitor_base& w) +{} +template +void out_degree(typename boost::graph_traits::vertex_descriptor v + , const Visitor_base& w) +{} +template +void in_degree(typename boost::graph_traits::vertex_descriptor v + , const Visitor_base& w) +{} +template +void source(typename boost::graph_traits::edge_descriptor e + , const Visitor_base & w) +{} +template +void target(typename boost::graph_traits::edge_descriptor e + , const Visitor_base & w) +{} +template +void edge(typename boost::graph_traits::vertex_descriptor u + , typename boost::graph_traits::vertex_descriptor v + , const Visitor_base & w) +{} +template +inline void vertices(const Visitor_base & w) +{} +template +inline void edges(const Visitor_base & w) +{} +template +inline void in_edges(typename boost::graph_traits::vertex_descriptor u + , const Visitor_base & w) +{} +template +inline void out_edges(typename boost::graph_traits::vertex_descriptor u + , const Visitor_base & w) +{} + +// +// MutableHalfedgeGraph +// +template +void add_vertex(Visitor_base & w) +{} +template +void add_vertex(const typename boost::graph_traits::vertex_property_type& p + , Visitor_base & w) +{} +template +void remove_vertex(typename boost::graph_traits< Graph >::vertex_descriptor v + , Visitor_base & w) +{} +template +void add_edge(Visitor_base & w) +{} +template +void remove_edge(typename boost::graph_traits< Graph >::edge_descriptor e + , Visitor_base & w) +{} +template +void set_target(typename boost::graph_traits< Graph >::halfedge_descriptor h1 + , typename boost::graph_traits< Graph >::vertex_descriptor v + , Visitor_base & w) +{} +template +void set_next(typename boost::graph_traits< Graph >::halfedge_descriptor h1 + , typename boost::graph_traits< Graph >::halfedge_descriptor h2 + , Visitor_base & w) +{} + +// +// MutableFaceGraph +// +template +void add_face(Visitor_base & w) +{} +template +void add_face(InputIterator begin, InputIterator end, + Visitor_base & w) +{} +template +void remove_face(typename boost::graph_traits< Graph >::face_descriptor f + , Visitor_base & w) +{} +template +void set_face(typename boost::graph_traits< Graph >::halfedge_descriptor h + , typename boost::graph_traits< Graph >::face_descriptor f + , const Visitor_base & w) +{} +template +void set_halfedge(typename boost::graph_traits< Graph >::face_descriptor f + , typename boost::graph_traits< Graph >::halfedge_descriptor h + , Visitor_base & w) +{} +template +void set_halfedge(typename boost::graph_traits< Graph >::vertex_descriptor v + , typename boost::graph_traits< Graph >::halfedge_descriptor h + , const Visitor_base & w) +{} + +// +// HalfedgeGraph +// +template +void edge(typename boost::graph_traits< Graph >::halfedge_descriptor h + , const Visitor_base & w) +{} +template +void halfedge(typename boost::graph_traits< Graph >::edge_descriptor e + , const Visitor_base & w) +{} +template +void halfedge(typename boost::graph_traits< Graph >::vertex_descriptor v + , const Visitor_base & w) +{} +template +void halfedge(typename boost::graph_traits< Graph >::vertex_descriptor u + , typename boost::graph_traits< Graph >::vertex_descriptor v + , const Visitor_base & w) +{} +template +void opposite(typename boost::graph_traits< Graph >::halfedge_descriptor h + , const Visitor_base & w) +{} +template +void source(typename boost::graph_traits< Graph >::halfedge_descriptor h + , const Visitor_base & w) +{} +template +void target(typename boost::graph_traits< Graph >::halfedge_descriptor h + , const Visitor_base & w) +{} +template +void next(typename boost::graph_traits< Graph >::halfedge_descriptor outedge + , const Visitor_base & w) +{} +template +void prev(typename boost::graph_traits< Graph >::halfedge_descriptor outedge + , const Visitor_base & w) +{} + +// +// HalfedgeListGraph +// +template +void halfedges(const Visitor_base & w) +{} +template +void num_halfedges(const Visitor_base & w) +{} + +// Graph +template +void face(typename boost::graph_traits< Graph >::halfedge_descriptor h + , const Visitor_base & w) +{} +template +void halfedge(typename boost::graph_traits< Graph >::face_descriptor f + , const Visitor_base & w) +{} +template +void faces(const Visitor_base & w) +{} +template +void num_faces(const Visitor_base & w) +{} +template +void is_valid(const Visitor_base & w, bool verbose = false) +{} +template +void get(PropertyTag ptag, const Visitor_base& w) +{} + + +//// OVERLOADS FOR TUPLE + +template +typename boost::graph_traits::vertices_size_type +num_vertices(const boost::tuple, + boost::reference_wrapper >& w) +{ + num_vertices(boost::unwrap_ref(w.get<0>())); + return num_vertices(boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits::edges_size_type +num_edges(const boost::tuple, + boost::reference_wrapper >& w) +{ + num_edges(boost::unwrap_ref(w.get<0>())); + return num_edges(boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits::degree_size_type +degree(typename boost::graph_traits::vertex_descriptor v + , const boost::tuple, + boost::reference_wrapper >& w) +{ + degree(v, boost::unwrap_ref(w.get<0>())); + return degree(v, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits::degree_size_type +out_degree(typename boost::graph_traits::vertex_descriptor v + , const boost::tuple, + boost::reference_wrapper >& w) +{ + out_degree(v, boost::unwrap_ref(w.get<0>())); + return out_degree(v, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits::degree_size_type +in_degree(typename boost::graph_traits::vertex_descriptor v + , const boost::tuple, + boost::reference_wrapper >& w) +{ + in_degree(v, boost::unwrap_ref(w.get<0>())); + return in_degree(v, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits::vertex_descriptor +source(typename boost::graph_traits::edge_descriptor e + , const boost::tuple, + boost::reference_wrapper > & w) +{ + source(e, boost::unwrap_ref(w.get<0>())); + return source(e, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits::vertex_descriptor +target(typename boost::graph_traits::edge_descriptor e + , const boost::tuple, + boost::reference_wrapper > & w) +{ + target(e, boost::unwrap_ref(w.get<0>())); + return target(e, boost::unwrap_ref(w.get<1>())); +} + +template +std::pair::edge_descriptor, bool> +edge(typename boost::graph_traits::vertex_descriptor u + , typename boost::graph_traits::vertex_descriptor v + , const boost::tuple, + boost::reference_wrapper > & w) +{ + edge(u, v, boost::unwrap_ref(w.get<0>())); + return edge(u, v, boost::unwrap_ref(w.get<1>())); +} + +template +inline CGAL::Iterator_range::vertex_iterator> +vertices(const boost::tuple, + boost::reference_wrapper >& w) +{ + vertices(boost::unwrap_ref(w.get<0>())); + return vertices(boost::unwrap_ref(w.get<1>())); +} + +template +inline CGAL::Iterator_range::edge_iterator> +edges(const boost::tuple, + boost::reference_wrapper >& w) +{ + edges(boost::unwrap_ref(w.get<0>())); + return edges(boost::unwrap_ref(w.get<1>())); +} + +template +inline CGAL::Iterator_range::in_edge_iterator> +in_edges(typename boost::graph_traits::vertex_descriptor u + , const boost::tuple, + boost::reference_wrapper >& w) +{ + in_edges(u, boost::unwrap_ref(w.get<0>())); + return in_edges(u, boost::unwrap_ref(w.get<1>())); +} + +template +inline CGAL::Iterator_range::out_edge_iterator> +out_edges(typename boost::graph_traits::vertex_descriptor u + , const boost::tuple, + boost::reference_wrapper >& w) +{ + out_edges(u, boost::unwrap_ref(w.get<0>())); + return out_edges(u, boost::unwrap_ref(w.get<1>())); +} + +// +// MutableHalfedgeGraph +// + +template +typename boost::graph_traits< Graph >::vertex_descriptor +add_vertex(boost::tuple, + boost::reference_wrapper >& w) +{ + add_vertex(boost::unwrap_ref(w.get<0>())); + return add_vertex(boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::vertex_descriptor +add_vertex(const typename boost::graph_traits::vertex_property_type& p + , boost::tuple, + boost::reference_wrapper >& w) +{ + add_vertex(p, boost::unwrap_ref(w.get<0>())); + return add_vertex(p, boost::unwrap_ref(w.get<1>())); +} + +template +void +remove_vertex(typename boost::graph_traits< Graph >::vertex_descriptor v + , boost::tuple, + boost::reference_wrapper >& w) +{ + remove_vertex(v, boost::unwrap_ref(w.get<0>())); + remove_vertex(v, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::edge_descriptor +add_edge(boost::tuple, + boost::reference_wrapper >& w) +{ + add_edge(boost::unwrap_ref(w.get<0>())); + return add_edge(boost::unwrap_ref(w.get<1>())); +} + +template +void +remove_edge(typename boost::graph_traits< Graph >::edge_descriptor e +, boost::tuple, + boost::reference_wrapper >& w) +{ + remove_edge(e, boost::unwrap_ref(w.get<0>())); + remove_edge(e, boost::unwrap_ref(w.get<1>())); +} + +template +void +set_target(typename boost::graph_traits< Graph >::halfedge_descriptor h1 +, typename boost::graph_traits< Graph >::vertex_descriptor v +, boost::tuple, + boost::reference_wrapper >& w) +{ + set_target(h1, v, boost::unwrap_ref(w.get<0>())); + set_target(h1, v, boost::unwrap_ref(w.get<1>())); +} + +template +void +set_next(typename boost::graph_traits< Graph >::halfedge_descriptor h1 + , typename boost::graph_traits< Graph >::halfedge_descriptor h2 + , boost::tuple, + boost::reference_wrapper >& w) +{ + set_next(h1, h2, boost::unwrap_ref(w.get<0>())); + set_next(h1, h2, boost::unwrap_ref(w.get<1>())); +} + +// +// MutableFaceGraph +// +template +typename boost::graph_traits< Graph >::face_descriptor +add_face(boost::tuple, + boost::reference_wrapper >& w) +{ + add_face(boost::unwrap_ref(w.get<0>())); + return add_face(boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::face_descriptor +add_face(InputIterator begin, + InputIterator end, + boost::tuple, + boost::reference_wrapper >& w) +{ + add_face(begin, end, boost::unwrap_ref(w.get<0>())); + return add_face(begin, end, boost::unwrap_ref(w.get<1>())); +} + +template +void +remove_face(typename boost::graph_traits< Graph >::face_descriptor f +, boost::tuple, +boost::reference_wrapper >& w) +{ + remove_face(f, boost::unwrap_ref(w.get<0>())); + return remove_face(f, boost::unwrap_ref(w.get<1>())); +} + +template +void +set_face(typename boost::graph_traits< Graph >::halfedge_descriptor h +, typename boost::graph_traits< Graph >::face_descriptor f +, const boost::tuple, +boost::reference_wrapper >& w) +{ + set_face(h, f, boost::unwrap_ref(w.get<0>())); + set_face(h, f, boost::unwrap_ref(w.get<1>())); +} + +template +void +set_halfedge(typename boost::graph_traits< Graph >::face_descriptor f +, typename boost::graph_traits< Graph >::halfedge_descriptor h +, boost::tuple, +boost::reference_wrapper >& w) +{ + set_halfedge(f, h, boost::unwrap_ref(w.get<0>())); + set_halfedge(f, h, boost::unwrap_ref(w.get<1>())); +} + +template +void +set_halfedge(typename boost::graph_traits< Graph >::vertex_descriptor v +, typename boost::graph_traits< Graph >::halfedge_descriptor h +, const boost::tuple, +boost::reference_wrapper >& w) +{ + set_halfedge(v, h, boost::unwrap_ref(w.get<0>())); + set_halfedge(v, h, boost::unwrap_ref(w.get<1>())); +} + +// +// HalfedgeGraph +// +template +typename boost::graph_traits< Graph >::edge_descriptor +edge(typename boost::graph_traits< Graph >::halfedge_descriptor h +, const boost::tuple, + boost::reference_wrapper >& w) +{ + edge(h, boost::unwrap_ref(w.get<0>())); + return edge(h, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::halfedge_descriptor +halfedge(typename boost::graph_traits< Graph >::edge_descriptor e +, const boost::tuple, + boost::reference_wrapper >& w) +{ + halfedge(e, boost::unwrap_ref(w.get<0>())); + return halfedge(e, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::halfedge_descriptor +halfedge(typename boost::graph_traits< Graph >::vertex_descriptor v +, const boost::tuple, + boost::reference_wrapper >& w) +{ + halfedge(v, boost::unwrap_ref(w.get<0>())); + return halfedge(v, boost::unwrap_ref(w.get<1>())); +} + +template +std::pair< typename boost::graph_traits< Graph >::halfedge_descriptor + , bool> + halfedge(typename boost::graph_traits< Graph >::vertex_descriptor u + , typename boost::graph_traits< Graph >::vertex_descriptor v + , const boost::tuple, + boost::reference_wrapper >& w) +{ + halfedge(u, v, boost::unwrap_ref(w.get<0>())); + return halfedge(u, v, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::halfedge_descriptor +opposite(typename boost::graph_traits< Graph >::halfedge_descriptor h +, const boost::tuple, + boost::reference_wrapper >& w) +{ + opposite(h, boost::unwrap_ref(w.get<0>())); + return opposite(h, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::vertex_descriptor +source(typename boost::graph_traits< Graph >::halfedge_descriptor h +, const boost::tuple, + boost::reference_wrapper >& w) +{ + source(h, boost::unwrap_ref(w.get<0>())); + return source(h, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::vertex_descriptor +target(typename boost::graph_traits< Graph >::halfedge_descriptor h +, const boost::tuple, boost::reference_wrapper >& w) +{ + target(h, boost::unwrap_ref(w.get<0>())); + return target(h, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::halfedge_descriptor +next(typename boost::graph_traits< Graph >::halfedge_descriptor outedge +, const boost::tuple, boost::reference_wrapper >& w) +{ + next(outedge, boost::unwrap_ref(w.get<0>())); + return next(outedge, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::halfedge_descriptor +prev(typename boost::graph_traits< Graph >::halfedge_descriptor outedge +, const boost::tuple, boost::reference_wrapper >& w) +{ + prev(outedge, boost::unwrap_ref(w.get<0>())); + return prev(outedge, boost::unwrap_ref(w.get<1>())); +} + +// +// HalfedgeListGraph +// +template +CGAL::Iterator_range::halfedge_iterator> +halfedges(const boost::tuple, boost::reference_wrapper >& w) +{ + halfedges(boost::unwrap_ref(w.get<0>())); + return halfedges(boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::halfedges_size_type +num_halfedges(const boost::tuple, boost::reference_wrapper >& w) +{ + num_halfedges(boost::unwrap_ref(w.get<0>())); + return num_halfedges(boost::unwrap_ref(w.get<1>())); +} + +// Graph +template +typename boost::graph_traits< Graph >::face_descriptor +face(typename boost::graph_traits< Graph >::halfedge_descriptor h +, const boost::tuple, boost::reference_wrapper >& w) +{ + face(h, boost::unwrap_ref(w.get<0>())); + return face(h, boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits< Graph >::halfedge_descriptor +halfedge(typename boost::graph_traits< Graph >::face_descriptor f +, const boost::tuple, boost::reference_wrapper >& w) +{ + halfedge(f, boost::unwrap_ref(w.get<0>())); + return halfedge(f, boost::unwrap_ref(w.get<1>())); +} + +template +inline CGAL::Iterator_range::face_iterator > +faces(const boost::tuple, boost::reference_wrapper >& w) +{ + faces(boost::unwrap_ref(w.get<0>())); + return faces(boost::unwrap_ref(w.get<1>())); +} + +template +typename boost::graph_traits::faces_size_type +num_faces(const boost::tuple, + boost::reference_wrapper >& w) +{ + num_faces(boost::unwrap_ref(w.get<0>())); + return num_faces(boost::unwrap_ref(w.get<1>())); +} + +template +bool is_valid(const boost::tuple, + boost::reference_wrapper >& w + , bool verbose = false) +{ + is_valid(boost::unwrap_ref(w.get<0>()), verbose); + return is_valid(boost::unwrap_ref(w.get<1>()), verbose); +} + +template +typename boost::property_map< Graph, PropertyTag >::type +get(PropertyTag ptag, + const boost::tuple, + boost::reference_wrapper >& w) +{ + get(ptag, boost::unwrap_ref(w.get<0>())); + return get(ptag, boost::unwrap_ref(w.get<1>())); +} + +}//end namespace CGAL + +#endif //CGAL_BOOST_GRAPH_VISITOR_H diff --git a/Documentation/biblio/cgal_manual.bib b/Documentation/biblio/cgal_manual.bib index 2590e3c8f52..b17a841fdfd 100644 --- a/Documentation/biblio/cgal_manual.bib +++ b/Documentation/biblio/cgal_manual.bib @@ -2597,6 +2597,15 @@ pages = "207--221" publisher={CRC press} } +@inproceedings{botsch2004remeshing, + title={A remeshing approach to multiresolution modeling}, + author={M. Botsch and L. Kobbelt}, + booktitle={Proceedings of the 2004 Eurographics/ACM SIGGRAPH symposium on Geometry processing}, + pages={185--192}, + year={2004}, + organization={ACM} +} + @inproceedings{liepa2003filling, title={Filling holes in meshes}, author={P. Liepa}, diff --git a/Installation/changes.html b/Installation/changes.html index 0b60b3f74f1..3e674281d42 100644 --- a/Installation/changes.html +++ b/Installation/changes.html @@ -156,12 +156,20 @@ and src/ directories).

2D Arrangements

  • Speed up the edge removal in case the incident faces contains many holes.
  • -
      +
    +

    Polygon Mesh Processing

    +
      +
    • Add a new triangle-based isotropic remeshing algorithm for + triangulated surface meshes, + CGAL::Polygon_mesh_processing::isotropic_remeshing() + and a helper function for isotropic remeshing : + CGAL::Polygon_mesh_processing::split_long_edges()
    • +

    Surface Mesh Parameterization

    • LSCM_parameterizer_3 now uses by default Eigen diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index 08890738b05..b97a2981eb5 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt @@ -112,15 +112,6 @@ typename CGAL::Kernel_traits< \endcode \cgalNPEnd -\cgalNPBegin{edge_is_constrained_map} \anchor PMP_edge_is_constrained_map - the property map containing information about edges of the input polygon mesh being constrained or not.\n -\b Type: a class model of `ReadablePropertyMap` with -`boost::graph_traits::%edge_descriptor` as key type and -`bool` as value type. It should be default - constructible.\n -\b Default: if this parameter is omitted, -a default property map where no edge is constrained is provided. -\cgalNPEnd - \cgalNPBegin{face_index_map} \anchor PMP_face_index_map the property map containing the index of each face of the input polygon mesh.\n \b Type: a class model of `ReadablePropertyMap` with @@ -137,6 +128,31 @@ a default property map where no edge is constrained is provided. \b Default value is \code boost::get(CGAL::vertex_index, pmesh)\endcode \cgalNPEnd +\cgalNPBegin{ number_of_iterations } \anchor PMP_number_of_iterations +the number of iterations of the sequence of iterations performed by the isotropic remeshing +algorithm.\n +\b Type : \c unsigned \c int \n +\b Default value is `1` +\cgalNPEnd + +\cgalNPBegin{ edge_is_constrained_map } \anchor PMP_edge_is_constrained_map +the property map containing information about edges of the input polygon mesh being constrained or not.\n +\b Type : a class model of `ReadablePropertyMap` with +`boost::graph_traits::%edge_descriptor` as key type and +`bool` as value type.It should be default - constructible.\n +\b Default : if this parameter is omitted, +a default property map where no edge is constrained is provided. +\cgalNPEnd + +\cgalNPBegin{protect_constraints} \anchor PMP_protect_constraints +enables the protection of constraints listed by \ref PMP_edge_is_constrained_map "edge_is_constrained_map" +during isotropic remeshing. If `true`, constraint edges cannot be modified at all +during the remeshing process.\n +\b Type : boolean \n +\b Default value is `false` +\cgalNPEnd + + \cgalNPTableEnd */ diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt index 95fa7251895..e4fb224ace0 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt @@ -9,6 +9,9 @@ /// path are not marked as constrained. /// /// \ingroup PkgPolygonMeshProcessing +/// \defgroup remeshing_grp Isotropic remeshing +/// \ingroup PkgPolygonMeshProcessing +/// \defgroup reverse_face_orientations_grp CGAL::reverse_face_orientations() /// \defgroup hole_filling_grp Hole Filling @@ -72,6 +75,8 @@ and provides a list of the parameters that are used in this package. - `CGAL::Polygon_mesh_processing::fair()` - `CGAL::Polygon_mesh_processing::refine()` - `CGAL::Polygon_mesh_processing::triangulate_faces()` +- \link remeshing_grp `CGAL::Polygon_mesh_processing::isotropic_remeshing()` \endlink +- \link remeshing_grp `CGAL::Polygon_mesh_processing::split_long_edges()` \endlink ## Hole Filling Functions ## - `CGAL::Polygon_mesh_processing::triangulate_hole()` @@ -86,15 +91,13 @@ and provides a list of the parameters that are used in this package. ## Orientation Functions ## - `CGAL::Polygon_mesh_processing::is_outward_oriented()` -- `CGAL::Polygon_mesh_processing::reverse_face_orientations()` +- \link reverse_face_orientations_grp `CGAL::Polygon_mesh_processing::reverse_face_orientations()` \endlink +- `CGAL::Polygon_mesh_processing::orient_polygon_soup()` ## Combinatorial Repairing Functions ## - \link PMP_repairing_grp `CGAL::Polygon_mesh_processing::stitch_borders()` \endlink - `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()` -- `CGAL::Polygon_mesh_processing::orient_polygon_soup()` - + ## Normal Computation Functions ## - `CGAL::Polygon_mesh_processing::compute_face_normal()` - `CGAL::Polygon_mesh_processing::compute_face_normals()` @@ -122,4 +125,5 @@ It can be made short to PM. And TriangleMesh (or TM) specifies when the paramete \todo document BGL/include/CGAL/boost/graph/Dual.h and remove the example from dont_submit \todo fix and restore remove_degenerate_faces in the user and the reference manual \todo publish `is_polygon_soup_a_polygon_mesh` defined in `polygon_soup_to_polygon_mesh.h` +\todo publish `triangulate_face`. Can be helpful as a preprocessing for isotropic_remeshing */ diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt index 73a022272d4..3202cac2def 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt @@ -37,7 +37,7 @@ BGL \ref namedparameters are used to deal with optional parameters. \subsection PMPOutline Outline The algorithms described in this manual are organized in sections. Section 2 describes \ref PMPMeshing algorithms, including triangulation of non-triangulated -meshes, refinement and optimization by fairing of triangulated surface meshes. +meshes, refinement, optimization by fairing, and isotropic remeshing of triangulated surface meshes. Section 3 lists the available, \ref PMPHoleFilling algorithms, which can possibly be combined with refinement and fairing. Section 4 gives a list of \ref PMPPredicates that can be evaluated on the processed polygon mesh, including point location and self intersection tests. @@ -70,9 +70,11 @@ The visual results of aforementioned steps are depicted by \cgalFigureRef{Mech_s \subsection MeshingAPI API -Refinement and fairing functions can be applied to an arbitrary region on a polygon mesh, using : -- `CGAL::Polygon_mesh_processing::refine()` : given a range of facets on a mesh, refines the region. -- `CGAL::Polygon_mesh_processing::fair()` : given a range of vertices on a mesh, fairs the region. +\subsubsection Meshing + +Refinement and fairing functions can be applied to an arbitrary region on a mesh, using : +- `CGAL::Polygon_mesh_processing::refine()` : given a set of facets on a mesh, refines the region. +- `CGAL::Polygon_mesh_processing::fair()` : given a set of vertices on a mesh, fairs the region. Fairing needs a sparse linear solver and we recommend the use of \ref thirdpartyEigen 3.2 or later. Note that fairing might fail if fixed vertices, which are used as boundary conditions, do @@ -91,6 +93,35 @@ This choice is made because the constrained Delaunay triangulation is the triangulation that, given the edges of the face to be triangulated, maximizes the minimum angle of all the angles of the triangles in the triangulation. +\subsubsection Remeshing + +The incremental triangle-based isotropic remeshing algorithm introduced by Botsch et al + \cgalCite{botsch2004remeshing}, \cgalCite{botsch2010PMP} is implemented in this package. +This algorithm incrementally performs simple operations such as edge splits, edge collapses, +edge flips, and Laplacian smoothing. All the vertices of the remeshed patch are reprojected +to the original surface to keep a good approximation of the input. + +A triangulated region of a polygon mesh can be remeshed using the function +`CGAL::Polygon_mesh_processing::isotropic_remeshing()`, as illustrated +by \cgalFigureRef{iso_remeshing}. The algorithm has only two parameters : +the target edge length for the remeshed surface patch, and +the number of iterations of the abovementioned sequence of operations. The bigger +this number, the smoother and closer to target edge length the mesh will be. + +An additional option has been added to \e protect (\e i.\e e. not modify) some given polylines. +In some cases, those polylines are too long, and reaching the desired target edge length while protecting them is not +possible and leads to an infinite loop of edge splits in the incident faces. To avoid that pitfall, the +function `CGAL::Polygon_mesh_processing::split_long_edges()` should be called on the list of +constrained edges before remeshing. + +\cgalFigureBegin{iso_remeshing, iso_remeshing.png} +Isotropic remeshing. (a) Triangulated input surface mesh. +(b) Surface uniformly and entirely remeshed. +(c) Selection of a range of faces to be remeshed. +(d) Surface mesh with the selection uniformly remeshed. +\cgalFigureEnd + + \subsection MeshingExamples Meshing Examples \subsubsection MeshingExample_1 Refine and Fair a Region on a Polygon Mesh @@ -108,6 +139,18 @@ as shown in the following example. \cgalExample{Polygon_mesh_processing/triangulate_faces_example.cpp} + +\subsubsection RemeshingExample_1 Isotropic Remeshing of a Region on a Polygon Mesh + +The following example shows a complete example of how the isotropic remeshing function can be used. +First, the border of the polygon mesh is collected. +Since the boundary edges will be considered as constrained and protected in this example, the function `split_long_edges()` is called first on these edges. + +Once this is done, remeshing is run on all the surface, with protection of constraints activated, for 3 iterations. + +\cgalExample{Polygon_mesh_processing/isotropic_remeshing_example.cpp} + + ******************************************** \section PMPHoleFilling Hole Filling @@ -270,6 +313,8 @@ or in general if its three vertices are collinear. The function `CGAL::Polygon_mesh_processing::remove_degenerate_faces()` removes those faces and fixes the connectivity of the newly cleaned up mesh. +It is also possible to remove isolated vertices from any polygon mesh, using the function +`CGAL::Polygon_mesh_processing::remove_isolated_vertices()`. \subsubsection RemoveDegenerateExample Example @@ -434,6 +479,8 @@ Function or Class | Pure Triangle | Self-Intersection Free `fair()` | no | no | no `refine()` | no | no | no `triangulate_faces()` | no | no | no +`isotropic_remeshing()` | no | no | no +`split_long_edges()` | no | no | no `triangulate_hole()` | no | no | no `triangulate_and_refine_hole()` | no | no | no `triangulate_refine_and_fair_hole()` | no | no | no @@ -446,6 +493,7 @@ Function or Class | Pure Triangle | Self-Intersection Free `stitch_borders()` | no | no | no `polygon_soup_to_polygon_mesh()` | no | no | no `orient_polygon_soup()` | no | no | no +`remove_isolated_vertices()` | no | no | no `compute_face_normal()` | no | no | no `compute_face_normals()` | no | no | no `compute_vertex_normal()` | no | no | no diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt index edb3b337e9e..a2339efc0ba 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt @@ -11,5 +11,5 @@ \example Polygon_mesh_processing/triangulate_polyline_example.cpp \example Polygon_mesh_processing/refine_fair_example.cpp \example Polygon_mesh_processing/mesh_slicer_example.cpp - +\example Polygon_mesh_processing/isotropic_remeshing_example.cpp */ diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/fig/iso_remeshing.png b/Polygon_mesh_processing/doc/Polygon_mesh_processing/fig/iso_remeshing.png new file mode 100644 index 00000000000..308d9227efe Binary files /dev/null and b/Polygon_mesh_processing/doc/Polygon_mesh_processing/fig/iso_remeshing.png differ diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 0ed41176ced..1e795107a15 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -82,6 +82,7 @@ create_single_source_cgal_program( "polygon_soup_example.cpp") create_single_source_cgal_program( "triangulate_polyline_example.cpp") create_single_source_cgal_program( "mesh_slicer_example.cpp") #create_single_source_cgal_program( "remove_degeneracies_example.cpp") +create_single_source_cgal_program( "isotropic_remeshing_example.cpp") if(NOT (${EIGEN3_VERSION} VERSION_LESS 3.2.0)) create_single_source_cgal_program( "hole_filling_example.cpp" ) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/isotropic_remeshing_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/isotropic_remeshing_example.cpp new file mode 100644 index 00000000000..ab237db5c3b --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/isotropic_remeshing_example.cpp @@ -0,0 +1,68 @@ +#include +#include +#include + +#include +#include + +#include +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; + +typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; +typedef boost::graph_traits::edge_descriptor edge_descriptor; + +struct halfedge2edge +{ + halfedge2edge(const Mesh& m, std::vector& edges) + : m_mesh(m), m_edges(edges) + {} + void operator()(const halfedge_descriptor& h) const + { + m_edges.push_back(edge(h, m_mesh)); + } + const Mesh& m_mesh; + std::vector& m_edges; +}; + +int main(int argc, char* argv[]) +{ + const char* filename = (argc > 1) ? argv[1] : "data/pig.off"; + std::ifstream input(filename); + + Mesh mesh; + if (!input || !(input >> mesh)) { + std::cerr << "Not a valid off file." << std::endl; + return 1; + } + + double target_edge_length = 0.04; + unsigned int nb_iter = 3; + + std::cout << "Split border..."; + + std::vector border; + PMP::border_halfedges(faces(mesh), + boost::make_function_output_iterator(halfedge2edge(mesh, border)), + mesh); + PMP::split_long_edges(mesh, border, target_edge_length); + + std::cout << "done." << std::endl; + + std::cout << "Start remeshing of " << filename + << " (" << num_faces(mesh) << " faces)..." << std::endl; + + PMP::isotropic_remeshing(mesh, + faces(mesh), + target_edge_length, + PMP::parameters::number_of_iterations(nb_iter) + .protect_constraints(true)//i.e. protect border, here + ); + + std::cout << "Remeshing done." << std::endl; + + return 0; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/border.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/border.h new file mode 100644 index 00000000000..9d58ae29c82 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/border.h @@ -0,0 +1,201 @@ +// Copyright (c) 2015 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// 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. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// +// Author(s) : Jane Tournois + +#ifndef CGAL_POLYGON_MESH_PROCESSING_GET_BORDER_H +#define CGAL_POLYGON_MESH_PROCESSING_GET_BORDER_H + +#include +#include +#include + +#include +#include + +#include + +namespace CGAL{ +namespace Polygon_mesh_processing { + + namespace internal + { + template + HalfedgeOutputIterator border_halfedges_impl(const FaceRange& faces + , HalfedgeOutputIterator out + , const PM& pmesh) + { + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + //collect halfedges that appear only once + // the bool is true if the halfedge stored is the one of the face, + // false if it is its opposite + std::map border; + BOOST_FOREACH(face_descriptor f, faces) + { + BOOST_FOREACH(halfedge_descriptor h, + halfedges_around_face(halfedge(f, pmesh), pmesh)) + { + //halfedge_descriptor is model of `LessThanComparable` + bool from_face = (h < opposite(h, pmesh)); + halfedge_descriptor he = from_face + ? h + : opposite(h, pmesh); + if (border.find(he) != border.end()) + border.erase(he); //even number of appearances + else + border.insert(std::make_pair(he, from_face));//odd number of appearances + } + } + //copy them in out + typedef typename std::map::value_type HD_bool; + BOOST_FOREACH(const HD_bool& hd, border) + { + if (hd.second) + *out++ = hd.first; + else + *out++ = opposite(hd.first, pmesh); + } + return out; + } + + template + HalfedgeOutputIterator border_halfedges_impl(const FaceRange& faces + , const FaceIndexMap& fmap + , HalfedgeOutputIterator out + , const PM& pmesh) + { + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + std::vector present(num_faces(pmesh), false); + BOOST_FOREACH(face_descriptor fd, faces) + present[get(fmap, fd)] = true; + + BOOST_FOREACH(face_descriptor fd, faces) + BOOST_FOREACH(halfedge_descriptor hd, + halfedges_around_face(halfedge(fd, pmesh), pmesh)) + { + halfedge_descriptor opp=opposite(hd, pmesh); + if (is_border(opp, pmesh) || !present[get(fmap,face(opp,pmesh))]) + *out++ = opp; + } + + return out; + } + + struct Dummy_PM + { + public: + typedef bool vertex_property_type; + }; + + }//end namespace internal + +#ifdef PMP_TODO_DOC + /*! + \ingroup PkgPolygonMeshProcessing + * collects the border of a surface patch + * defined as a face range. The border is "seen from inside" the patch, + * i.e. the collected halfedges are + * the ones that belong to the input faces. + * + * @tparam PolygonMesh model of `HalfedgeGraph` + * @tparam FaceRange range of + `boost::graph_traits::%face_descriptor`, model of `Range`. + Its iterator type is `InputIterator`. + * @tparam HalfedgeOutputIterator model of `OutputIterator` + holding `boost::graph_traits::%halfedge_descriptor` + for patch border + * @tparam NamedParameters a sequence of \ref namedparameters + * + * @param pmesh the polygon mesh to which `faces` belong + * @param faces the range of faces defining the patch + * around which the border is collected + * @param out the output iterator that collects halfedges that form the border + * of `faces`, seen from inside the surface patch + * @param np optional sequence of \ref namedparameters among + the ones listed below + + * \cgalNamedParamsBegin + \cgalParamBegin{face_index_map} a property map containing the index of each face of `pmesh` \cgalParamEnd + \cgalNamedParamsEnd + * + * @returns `out` + */ +#endif + template + HalfedgeOutputIterator border_halfedges(const FaceRange& faces + , HalfedgeOutputIterator out + , const PolygonMesh& pmesh + , const NamedParameters& np) + { + typedef PolygonMesh PM; + typedef typename GetFaceIndexMap::type FIMap; + typedef typename boost::property_map::type Unset_FIMap; + + if (boost::is_same::value) + { + //face index map is not given in named parameters, nor as an internal property map + return internal::border_halfedges_impl(faces, out, pmesh); + } + //face index map given as a named parameter, or as an internal property map + FIMap fim = choose_param(get_param(np, face_index), + get(CGAL::face_index, pmesh)); + return internal::border_halfedges_impl(faces, fim, out, pmesh); + } + + template + HalfedgeOutputIterator border_halfedges(HalfedgeOutputIterator out + , const PolygonMesh& pmesh) + { + typedef PolygonMesh PM; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + BOOST_FOREACH(halfedge_descriptor hd, halfedges(pmesh)) + if (is_border(hd, pmesh)) + *out++ = hd; + return out; + } + + template + HalfedgeOutputIterator border_halfedges(const FaceRange& faces + , HalfedgeOutputIterator out + , const PolygonMesh& pmesh) + { + return border_halfedges(faces, out, pmesh, + CGAL::Polygon_mesh_processing::parameters::all_default()); + } + +} +} // end of namespace CGAL::Polygon_mesh_processing + + +#endif //CGAL_POLYGON_MESH_PROCESSING_GET_BORDER_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/compute_normal.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/compute_normal.h index d8c64cdc1c8..e5256e16725 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/compute_normal.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/compute_normal.h @@ -94,11 +94,11 @@ compute_face_normal(typename boost::graph_traits::face_descriptor f typedef typename Kernel::Vector_3 Vector; using boost::get_param; - using boost::choose_param; + using boost::choose_const_pmap; Vector normal = CGAL::NULL_VECTOR; sum_normals(pmesh, f - , choose_param(get_param(np, vertex_point), get(CGAL::vertex_point, pmesh)) + , choose_const_pmap(get_param(np, CGAL::vertex_point), pmesh, CGAL::vertex_point) , normal); return normal / FT( std::sqrt( to_double(normal * normal) ) ); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h index 819063336ce..81979fb6c6b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h @@ -438,7 +438,8 @@ connected_component(typename boost::graph_traits::face_descriptor s internal::No_constraint//default > ::type EdgeConstraintMap; EdgeConstraintMap ecmap - = choose_param(get_param(np, edge_is_constrained), EdgeConstraintMap()); + = choose_param(get_param(np, edge_is_constrained), + internal::No_constraint()); typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/named_function_params.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/named_function_params.h index 4c6a1041983..a6f25238862 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/named_function_params.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/named_function_params.h @@ -33,6 +33,8 @@ namespace CGAL{ enum fairing_continuity_t { fairing_continuity }; enum sparse_linear_solver_t { sparse_linear_solver }; enum geom_traits_t { geom_traits }; + enum number_of_iterations_t { number_of_iterations }; + enum protect_constraints_t { protect_constraints }; //internal enum weight_calculator_t { weight_calculator }; @@ -112,6 +114,22 @@ namespace CGAL{ return Params(k, *this); } + template + pmp_bgl_named_params + number_of_iterations(const NT& n) const + { + typedef pmp_bgl_named_params Params; + return Params(n, *this); + } + + template + pmp_bgl_named_params + protect_constraints(const Boolean b) const + { + typedef pmp_bgl_named_params Params; + return Params(b, *this); + } + //overload template pmp_bgl_named_params @@ -218,6 +236,22 @@ namespace parameters{ return Params(k); } + template + pmp_bgl_named_params + number_of_iterations(const NT& n) + { + typedef pmp_bgl_named_params Params; + return Params(n); + } + + template + pmp_bgl_named_params + protect_constraints(const Boolean b) + { + typedef pmp_bgl_named_params Params; + return Params(b); + } + //overload template pmp_bgl_named_params diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/remesh_impl.h new file mode 100644 index 00000000000..cebe8962503 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/remesh_impl.h @@ -0,0 +1,1347 @@ +// Copyright (c) 2015 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// 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. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// +// Author(s) : Jane Tournois + +#ifndef CGAL_POLYGON_MESH_PROCESSING_REMESH_IMPL_H +#define CGAL_POLYGON_MESH_PROCESSING_REMESH_IMPL_H + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CGAL_PMP_REMESHING_DEBUG +#include +#define CGAL_DUMP_REMESHING_STEPS +#endif + +#ifdef CGAL_PMP_REMESHING_VERY_VERBOSE +#define CGAL_PMP_REMESHING_VERBOSE +#endif + + +namespace PMP = CGAL::Polygon_mesh_processing; + + +namespace CGAL { +namespace Polygon_mesh_processing { +namespace internal { + + enum Halfedge_status { + PATCH, //h and hopp belong to the patch to be remeshed + PATCH_BORDER,//h belongs to the patch, hopp is MESH + MESH, //h and hopp belong to the mesh, not the patch + MESH_BORDER //h belongs to the mesh, face(hopp, pmesh) == null_face() + }; + + // A property map + template + struct Border_constraint_pmap + { + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + + std::map border_edges; + const PM& pmesh_; + public: + Border_constraint_pmap(const PM& pmesh, const FaceRange& faces) + : pmesh_(pmesh) + { + std::vector border; + PMP::border_halfedges(faces, std::back_inserter(border), pmesh_); + + BOOST_FOREACH(edge_descriptor e, edges(pmesh_)) + border_edges.insert(std::make_pair(e, false)); + + BOOST_FOREACH(halfedge_descriptor h, border) + border_edges[edge(h, pmesh_)] = true; + } + + friend bool get(const Border_constraint_pmap& map, + const edge_descriptor& e) + { + CGAL_assertion(!map.border_edges.empty()); + typename std::map::const_iterator it + = map.border_edges.find(e); + + CGAL_assertion(it != map.border_edges.end()); + return it->second; + } + + }; + + template + bool constraints_are_short_enough(const PM& pmesh, + EdgeConstraintMap ecmap, + VertexPointMap vpmap, + const double& high) + { + double sqh = high*high; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + BOOST_FOREACH(edge_descriptor e, edges(pmesh)) + { + if (get(ecmap, e)) + { + halfedge_descriptor h = halfedge(e, pmesh); + if (sqh < CGAL::squared_distance(get(vpmap, source(h, pmesh)), + get(vpmap, target(h, pmesh)))) + { + return false; + } + } + } + return true; + } + + template + class Incremental_remesher + { + typedef PolygonMesh PM; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef typename GeomTraits::Point_3 Point; + typedef typename GeomTraits::Vector_3 Vector_3; + typedef typename GeomTraits::Plane_3 Plane_3; + typedef typename GeomTraits::Triangle_3 Triangle_3; + + typedef std::list Triangle_list; + typedef typename Triangle_list::iterator Tr_iterator; + typedef CGAL::AABB_triangle_primitive Primitive; + typedef CGAL::AABB_traits Traits; + typedef CGAL::AABB_tree AABB_tree; + + public: + Incremental_remesher(PolygonMesh& pmesh + , VertexPointMap& vpmap + , const bool protect_constraints) + : mesh_(pmesh) + , vpmap_(vpmap) + , own_tree_(true) + , input_triangles_() + , halfedge_status_map_() + , protect_constraints_(protect_constraints) + { + CGAL_assertion(CGAL::is_triangle_mesh(mesh_)); + + //build AABB tree of input surface + //todo : add a constructor with aabb_tree as parameter + //todo : do we really need to keep this copy of the input surface? + BOOST_FOREACH(face_descriptor f, faces(mesh_)) + { + halfedge_descriptor h = halfedge(f, mesh_); + vertex_descriptor v1 = target(h, mesh_); + vertex_descriptor v2 = target(next(h, mesh_), mesh_); + vertex_descriptor v3 = target(next(next(h, mesh_), mesh_), mesh_); + input_triangles_.push_back( + Triangle_3(get(vpmap, v1), get(vpmap, v2), get(vpmap, v3))); + } + tree_ptr_ = new AABB_tree(input_triangles_.begin(), input_triangles_.end()); + tree_ptr_->accelerate_distance_queries(); + } + + ~Incremental_remesher() + { + if (own_tree_) + delete tree_ptr_; + } + + template + void init_faces_remeshing(const FaceRange& face_range + , const EdgeIsConstrainedMap& ecmap) + { + tag_halfedges_status(face_range, ecmap); + } + + // split edges of edge_range that have their length > high + template + void split_long_edges(const EdgeRange& edge_range, + const double& high, + OutputIterator out)//new edges, replacing edge_range + { + typedef boost::bimap< + boost::bimaps::set_of, + boost::bimaps::multiset_of > > Boost_bimap; + typedef typename Boost_bimap::value_type long_edge; + +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "Split long edges (" << high << ")..."; + std::cout.flush(); +#endif + double sq_high = high*high; + + //collect long edges + Boost_bimap long_edges; + BOOST_FOREACH(edge_descriptor e, edge_range) + { + double sqlen = sqlength(e); + if (sqlen > sq_high) + long_edges.insert(long_edge(halfedge(e, mesh_), sqlen)); + else + *out++ = e; + } + + //split long edges + unsigned int nb_splits = 0; + while (!long_edges.empty()) + { + //the edge with longest length + typename Boost_bimap::right_map::iterator eit = long_edges.right.begin(); + halfedge_descriptor he = eit->second; + double sqlen = eit->first; + long_edges.right.erase(eit); + + //split edge + Point refinement_point = this->midpoint(he); + halfedge_descriptor hnew = CGAL::Euler::split_edge(he, mesh_); + CGAL_assertion(he == next(hnew, mesh_)); + ++nb_splits; + + //move refinement point + vertex_descriptor vnew = target(hnew, mesh_); + put(vpmap_, vnew, refinement_point); +#ifdef CGAL_PMP_REMESHING_VERY_VERBOSE + std::cout << " refinement point : " << refinement_point << std::endl; +#endif + + //check sub-edges + double sqlen_new = 0.25 * sqlen; + if (sqlen_new > sq_high) + { + //if it was more than twice the "long" threshold, insert them + long_edges.insert(long_edge(hnew, sqlen_new)); + long_edges.insert(long_edge(next(hnew, mesh_), sqlen_new)); + } + else + { + *out++ = edge(hnew, mesh_); + *out++ = edge(next(hnew, mesh_), mesh_); + } + + //insert new edges to keep triangular faces, and update long_edges + if (!is_border(hnew, mesh_)) + { + CGAL::Euler::split_face(hnew, next(next(hnew, mesh_), mesh_), mesh_); + } + + //do it again on the other side if we're not on boundary + halfedge_descriptor hnew_opp = opposite(hnew, mesh_); + if (!is_border(hnew_opp, mesh_)) + { + CGAL::Euler::split_face(prev(hnew_opp, mesh_), next(hnew_opp, mesh_), mesh_); + } + } +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << " done (" << nb_splits << " splits)." << std::endl; +#endif +#ifdef CGAL_DUMP_REMESHING_STEPS + dump("0-border_split.off"); +#endif + } + + // PMP book : + // "visits all edges of the mesh + //if an edge is longer than the given threshold `high`, the edge + //is split at its midpoint and the two adjacent triangles are bisected (2-4 split)" + void split_long_edges(const double& high) + { + typedef boost::bimap< + boost::bimaps::set_of, + boost::bimaps::multiset_of > > Boost_bimap; + typedef typename Boost_bimap::value_type long_edge; + +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "Split long edges (" << high << ")..." << std::endl; +#endif + double sq_high = high*high; + + //collect long edges + Boost_bimap long_edges; + BOOST_FOREACH(edge_descriptor e, edges(mesh_)) + { + if (!is_split_allowed(e)) + continue; + double sqlen = sqlength(e); + if(sqlen > sq_high) + long_edges.insert(long_edge(halfedge(e, mesh_), sqlen)); + } + + //split long edges + unsigned int nb_splits = 0; + while (!long_edges.empty()) + { + //the edge with longest length + typename Boost_bimap::right_map::iterator eit = long_edges.right.begin(); + halfedge_descriptor he = eit->second; + double sqlen = eit->first; + long_edges.right.erase(eit); + +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "\r\t(" << long_edges.left.size() << " long edges, "; + std::cout << nb_splits << " splits)"; + std::cout.flush(); +#endif + + if (protect_constraints_ && !is_longest_on_faces(edge(he, mesh_))) + continue; + + //split edge + Point refinement_point = this->midpoint(he); + halfedge_descriptor hnew = CGAL::Euler::split_edge(he, mesh_); + CGAL_assertion(he == next(hnew, mesh_)); + ++nb_splits; + + //move refinement point + vertex_descriptor vnew = target(hnew, mesh_); + put(vpmap_, vnew, refinement_point); +#ifdef CGAL_PMP_REMESHING_VERY_VERBOSE + std::cout << " Refinement point : " << refinement_point << std::endl; +#endif + + //after splitting + halfedge_descriptor hnew_opp = opposite(hnew, mesh_); + halfedge_added(hnew, status(he)); + halfedge_added(hnew_opp, status(opposite(he, mesh_))); + + //check sub-edges + double sqlen_new = 0.25 * sqlen; + if (sqlen_new > sq_high) + { + //if it was more than twice the "long" threshold, insert them + long_edges.insert(long_edge(hnew, sqlen_new)); + long_edges.insert(long_edge(next(hnew, mesh_), sqlen_new)); + } + + //insert new edges to keep triangular faces, and update long_edges + if (!is_on_border(hnew)) + { + halfedge_descriptor hnew2 = CGAL::Euler::split_face(hnew, + next(next(hnew, mesh_), mesh_), + mesh_); + Halfedge_status snew = (is_on_patch(hnew) || is_on_patch_border(hnew)) + ? PATCH + : MESH; + halfedge_added(hnew2, snew); + halfedge_added(opposite(hnew2, mesh_), snew); + + if (snew == PATCH) + { + double sql = sqlength(hnew2); + if (sql > sq_high) + long_edges.insert(long_edge(hnew2, sql)); + } + } + + //do it again on the other side if we're not on boundary + if (!is_on_border(hnew_opp)) + { + halfedge_descriptor hnew2 = CGAL::Euler::split_face(prev(hnew_opp, mesh_), + next(hnew_opp, mesh_), + mesh_); + Halfedge_status snew = (is_on_patch(hnew_opp) || is_on_patch_border(hnew_opp)) + ? PATCH + : MESH; + halfedge_added(hnew2, snew); + halfedge_added(opposite(hnew2, mesh_), snew); + + if (snew == PATCH) + { + double sql = sqlength(hnew2); + if (sql > sq_high) + long_edges.insert(long_edge(hnew2, sql)); + } + } + } +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << " done ("<< nb_splits << " splits)." << std::endl; +#endif + +#ifdef CGAL_PMP_REMESHING_DEBUG + CGAL_expensive_assertion(is_triangle_mesh(mesh_)); + CGAL_assertion(halfedge_status_map_.size() == nb_valid_halfedges()); + debug_status_map(); + debug_self_intersections(); +#endif + +#ifdef CGAL_DUMP_REMESHING_STEPS + dump("1-edge_split.off"); +#endif + } + + // PMP book : + // "collapses and thus removes all edges that are shorter than a + // threshold `low`. [...] testing before each collapse whether the collapse + // would produce an edge that is longer than `high`" + void collapse_short_edges(const double& low, const double& high) + { + typedef boost::bimap< + boost::bimaps::set_of, + boost::bimaps::multiset_of > > Boost_bimap; + typedef typename Boost_bimap::value_type short_edge; + +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "Collapse short edges (" << low << ", " << high << ")..." + << std::endl; +#endif + double sq_low = low*low; + double sq_high = high*high; + + Boost_bimap short_edges; + BOOST_FOREACH(edge_descriptor e, edges(mesh_)) + { + if (!is_collapse_allowed(e)) + continue; + double sqlen = sqlength(e); + if (sqlen < sq_low) + short_edges.insert(short_edge(halfedge(e, mesh_), sqlen)); + } + + unsigned int nb_collapses = 0; + while (!short_edges.empty()) + { + //the edge with shortest length + typename Boost_bimap::right_map::iterator eit = short_edges.right.begin(); + halfedge_descriptor he = eit->second; + short_edges.right.erase(eit); + +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "\r\t(" << short_edges.left.size() << " short edges, "; + std::cout << nb_collapses << " collapses)"; + std::cout.flush(); +#endif + + //handle the boundary case : + //a PATCH_BORDER edge can be collapsed, + //and an edge incident to PATCH_BORDER can be collapsed, + //but only if the boundary vertex is kept, + //so re-insert opposite(he) to collapse it + if (!is_on_patch(he)) + { + CGAL_assertion(!protect_constraints_);//is_collapse_allowed returned false + if (is_on_border(he) || is_on_mesh(he)) + { + he = opposite(he, mesh_); //he now is PATCH_BORDER + CGAL_assertion(is_on_patch_border(he)); + } + }//end if(not on PATCH) + + //let's try to collapse he into vb + vertex_descriptor va = source(he, mesh_); + vertex_descriptor vb = target(he, mesh_); + + if (is_on_patch_border(va) && !is_on_patch_border(vb)) + { + he = opposite(he, mesh_); + va = source(he, mesh_); + vb = target(he, mesh_); + CGAL_assertion(is_on_patch_border(vb) && !is_on_patch_border(va)); + } + else if (is_on_patch(va) && is_on_patch(vb)) + { + if(!collapse_does_not_invert_face(he)) + { + if (collapse_does_not_invert_face(opposite(he, mesh_))) + { + he = opposite(he, mesh_); + va = source(he, mesh_); + vb = target(he, mesh_); + } + else + continue;//both directions invert a face + } + CGAL_assertion(collapse_does_not_invert_face(he)); + } + + CGAL_assertion(is_collapse_allowed(edge(he, mesh_))); + + if (degree(va, mesh_) < 3 + || degree(vb, mesh_) < 3 + || !CGAL::Euler::satisfies_link_condition(edge(he, mesh_), mesh_))//necessary to collapse + continue; + + //check that collapse would not create an edge with length > high + //iterate on vertices va_i of the one-ring of va + bool collapse_ok = true; + BOOST_FOREACH(halfedge_descriptor ha, halfedges_around_target(va, mesh_)) + { + vertex_descriptor va_i = source(ha, mesh_); + if (sqlength(vb, va_i) > sq_high) + { + collapse_ok = false; + break; + } + } + //if it is allowed, perform the collapse + if (collapse_ok) + { + //"collapse va into vb along e" + // remove edges incident to va and vb, because their lengths will change + BOOST_FOREACH(halfedge_descriptor ha, halfedges_around_target(va, mesh_)) + { + short_edges.left.erase(ha); + short_edges.left.erase(opposite(ha, mesh_)); + } + BOOST_FOREACH(halfedge_descriptor hb, halfedges_around_target(vb, mesh_)) + { + short_edges.left.erase(hb); + short_edges.left.erase(opposite(hb, mesh_)); + } + + //before collapse + bool mesh_border_case = is_on_border(opposite(he, mesh_)); + halfedge_descriptor ep_p = prev(opposite(he, mesh_), mesh_); + halfedge_descriptor epo_p = opposite(ep_p, mesh_); + halfedge_descriptor en = next(he, mesh_); + halfedge_descriptor en_p = next(opposite(he, mesh_), mesh_); + Halfedge_status s_ep_p = status(ep_p); + Halfedge_status s_epo_p = status(epo_p); + Halfedge_status s_ep = status(prev(he, mesh_)); + Halfedge_status s_epo = status(opposite(prev(he, mesh_), mesh_)); + + // merge halfedge_status to keep the more important on both sides + //do it before collapse is performed to be sure everything is valid + merge_status(en, s_epo, s_ep); + if (!mesh_border_case) + merge_status(en_p, s_epo_p, s_ep_p); + + if (!mesh_border_case) + halfedge_and_opp_removed(prev(opposite(he, mesh_), mesh_)); + halfedge_and_opp_removed(he); + halfedge_and_opp_removed(prev(he, mesh_)); + + //perform collapse + Point target_point = get(vpmap_, vb); + vertex_descriptor vkept = CGAL::Euler::collapse_edge(edge(he, mesh_), mesh_); + put(vpmap_, vkept, target_point); + ++nb_collapses; + + fix_degenerate_faces(vkept, short_edges, sq_low); + +#ifdef CGAL_PMP_REMESHING_DEBUG + CGAL_assertion_code(std::size_t nbb = nb_valid_halfedges()); + CGAL_assertion(nbb == halfedge_status_map_.size()); + debug_status_map(); + CGAL_assertion(!incident_to_degenerate(halfedge(vkept, mesh_))); +#endif + + //insert new/remaining short edges + BOOST_FOREACH(halfedge_descriptor ht, halfedges_around_target(vkept, mesh_)) + { + if (!is_collapse_allowed(edge(ht, mesh_))) + continue; + double sqlen = sqlength(ht); + if (sqlen < sq_low) + short_edges.insert(short_edge(ht, sqlen)); + } + }//end if(collapse_ok) + } + +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << " done (" << nb_collapses << " collapses)." << std::endl; +#endif + +#ifdef CGAL_DUMP_REMESHING_STEPS + dump("2-edge_collapse.off"); +#endif + +#ifdef CGAL_PMP_REMESHING_DEBUG + CGAL_assertion(nb_valid_halfedges() == halfedge_status_map_.size()); + CGAL_expensive_assertion(is_triangle_mesh(mesh_)); + debug_status_map(); + debug_self_intersections(); + CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_, + PMP::parameters::vertex_point_map(vpmap_).geom_traits(GeomTraits()))); +#endif + } + + // PMP book : + // "equalizes the vertex valences by flipping edges. + // The target valence is 6 and 4 for interior and boundary vertices, resp. + // The algo. tentatively flips each edge `e` and checks whether the deviation + // to the target valences decreases. If not, the edge is flipped back" + void equalize_valences() + { +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "Equalize valences..."; + std::cout.flush(); +#endif + unsigned int nb_flips = 0; + BOOST_FOREACH(edge_descriptor e, edges(mesh_)) + { + //only the patch edges are allowed to be flipped + halfedge_descriptor he = halfedge(e, mesh_); + if (!is_on_patch(he)) + continue; + + vertex_descriptor va = source(he, mesh_); + vertex_descriptor vb = target(he, mesh_); + vertex_descriptor vc = target(next(he, mesh_), mesh_); + vertex_descriptor vd = target(next(opposite(he, mesh_), mesh_), mesh_); + + int deviation_pre = CGAL::abs(valence(va) - target_valence(va)) + + CGAL::abs(valence(vb) - target_valence(vb)) + + CGAL::abs(valence(vc) - target_valence(vc)) + + CGAL::abs(valence(vd) - target_valence(vd)); + + CGAL_assertion_code(Halfedge_status s1 = status(he)); + CGAL_assertion_code(Halfedge_status s1o = status(opposite(he, mesh_))); + CGAL_assertion(!incident_to_degenerate(he)); + CGAL_assertion(!incident_to_degenerate(opposite(he, mesh_))); + + CGAL::Euler::flip_edge(he, mesh_); + ++nb_flips; + + CGAL_assertion_code(Halfedge_status s2 = status(he)); + CGAL_assertion_code(Halfedge_status s2o = status(opposite(he, mesh_))); + CGAL_assertion(s1 == s2 && s1 == PATCH); + CGAL_assertion(s1o == s2o && s1o == PATCH); + CGAL_assertion(nb_valid_halfedges() == halfedge_status_map_.size()); + CGAL_assertion(!is_border(he, mesh_)); + + CGAL_assertion( + (vc == target(he, mesh_) && vd == source(he, mesh_)) + || (vd == target(he, mesh_) && vc == source(he, mesh_))); + + int deviation_post = CGAL::abs(valence(va) - target_valence(va)) + + CGAL::abs(valence(vb) - target_valence(vb)) + + CGAL::abs(valence(vc) - target_valence(vc)) + + CGAL::abs(valence(vd) - target_valence(vd)); + + //check that mesh does not become non-triangle, + //nor has inverted faces + if (deviation_pre < deviation_post + || !check_normals(he) + || incident_to_degenerate(he) + || incident_to_degenerate(opposite(he, mesh_)) + || !is_on_triangle(he) + || !is_on_triangle(opposite(he, mesh_)) + || !check_normals(target(he, mesh_)) + || !check_normals(source(he, mesh_))) + { + CGAL::Euler::flip_edge(he, mesh_); + --nb_flips; + + CGAL_assertion_code(Halfedge_status s3 = status(he)); + CGAL_assertion(s1 == s3); + CGAL_assertion(!is_border(he, mesh_)); + CGAL_assertion( + (va == source(he, mesh_) && vb == target(he, mesh_)) + || (vb == source(he, mesh_) && va == target(he, mesh_))); + } + } + +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "done. ("<< nb_flips << " flips)" << std::endl; +#endif + +#ifdef CGAL_PMP_REMESHING_DEBUG + CGAL_assertion(nb_valid_halfedges() == halfedge_status_map_.size()); + debug_status_map(); + CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_ + , PMP::parameters::vertex_point_map(vpmap_) + .geom_traits(GeomTraits()))); + debug_self_intersections(); +#endif + +#ifdef CGAL_DUMP_REMESHING_STEPS + dump("3-edge_flips.off"); +#endif + } + + // PMP book : + // "applies an iterative smoothing filter to the mesh. + // The vertex movement has to be constrained to the vertex tangent plane [...] + // smoothing algorithm with uniform Laplacian weights" + void tangential_relaxation() + { + //todo : move border vertices along 1-dimensional features +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "Tangential relaxation..."; + std::cout.flush(); +#endif + + //todo : use boost::vector_property_map to improve computing time + typedef std::map VNormalsMap; + VNormalsMap vnormals; + boost::associative_property_map propmap_normals(vnormals); + BOOST_FOREACH(vertex_descriptor v, vertices(mesh_)) + { + if (!is_on_patch(v)) + continue; + Vector_3 vn = PMP::compute_vertex_normal(v, mesh_ + , PMP::parameters::vertex_point_map(vpmap_) + .geom_traits(GeomTraits())); + put(propmap_normals, v, vn); + } + + // at each vertex, compute barycenter of neighbors + std::map barycenters; + BOOST_FOREACH(vertex_descriptor v, vertices(mesh_)) + { + if (!is_on_patch(v)) + continue; + Vector_3 move = CGAL::NULL_VECTOR; + unsigned int star_size = 0; + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, mesh_)) + { + move = move + Vector_3(get(vpmap_, v), get(vpmap_, source(h, mesh_))); + ++star_size; + } + CGAL_assertion(star_size > 0); + move = (1. / (double)star_size) * move; + + barycenters[v] = get(vpmap_, v) + move; + } + + // compute moves + typedef typename std::map::value_type VP_pair; + std::map new_locations; + BOOST_FOREACH(const VP_pair& vp, barycenters) + { + vertex_descriptor v = vp.first; + Point pv = get(vpmap_, v); + Vector_3 nv = boost::get(propmap_normals, v); + Point qv = vp.second; //barycenter at v + + new_locations[v] = qv + (nv * Vector_3(qv, pv)) * nv; + } + + // perform moves + BOOST_FOREACH(const VP_pair& vp, new_locations) + { + put(vpmap_, vp.first, vp.second); + } + + CGAL_assertion(is_valid(mesh_)); + + CGAL_assertion(is_triangle_mesh(mesh_)); +#ifdef CGAL_PMP_REMESHING_DEBUG + debug_self_intersections(); +#endif +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "done." << std::endl; +#endif +#ifdef CGAL_DUMP_REMESHING_STEPS + dump("4-relaxation.off"); +#endif + } + + + // PMP book : + // "maps the vertices back to the surface" + void project_to_surface() + { + //todo : handle the case of boundary vertices +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "Project to surface..."; + std::cout.flush(); +#endif + + BOOST_FOREACH(vertex_descriptor v, vertices(mesh_)) + { + if (!is_on_patch(v)) + continue; + put(vpmap_, v, tree_ptr_->closest_point(get(vpmap_, v))); + } + + CGAL_assertion(is_valid(mesh_)); + CGAL_assertion(is_triangle_mesh(mesh_)); + +#ifdef CGAL_PMP_REMESHING_DEBUG + debug_self_intersections(); +#endif +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "done." << std::endl; +#endif + +#ifdef CGAL_DUMP_REMESHING_STEPS + dump("5-project.off"); +#endif + } + + private: + double sqlength(const vertex_descriptor& v1, + const vertex_descriptor& v2) const + { + return CGAL::squared_distance(get(vpmap_, v1), get(vpmap_, v2)); + } + + double sqlength(const halfedge_descriptor& h) const + { + vertex_descriptor v1 = target(h, mesh_); + vertex_descriptor v2 = source(h, mesh_); + return sqlength(v1, v2); + } + + double sqlength(const edge_descriptor& e) const + { + return sqlength(halfedge(e, mesh_)); + } + + Point midpoint(const halfedge_descriptor& he) const + { + Point p1 = get(vpmap_, target(he, mesh_)); + Point p2 = get(vpmap_, source(he, mesh_)); + return CGAL::midpoint(p1, p2); + } + + void dump(const char* filename) const + { + std::ofstream out(filename); +// out << mesh_; + out.close(); + } + + int valence(const vertex_descriptor& v) const + { + return static_cast(degree(v, mesh_)); + } + + int target_valence(const vertex_descriptor& v) const + { + return (is_border(v, mesh_)) ? 4 : 6; + } + + bool is_on_triangle(const halfedge_descriptor& h) const + { + return h == next(next(next(h, mesh_), mesh_), mesh_); + } + + bool is_longest_on_faces(const edge_descriptor& e) const + { + halfedge_descriptor h = halfedge(e, mesh_); + halfedge_descriptor hopp = opposite(h, mesh_); + + //check whether h is the longest edge in its associated face + //overwise refinement will go for an endless loop + double sqh = sqlength(h); + return sqh >= sqlength(next(h, mesh_)) + && sqh >= sqlength(next(next(h, mesh_), mesh_)) + //do the same for hopp + && sqh >= sqlength(next(hopp, mesh_)) + && sqh >= sqlength(next(next(hopp, mesh_), mesh_)); + } + + bool is_split_allowed(const edge_descriptor& e) const + { + halfedge_descriptor h = halfedge(e, mesh_); + halfedge_descriptor hopp = opposite(h, mesh_); + + if (protect_constraints_) + { + return is_on_patch(h); //PATCH are the only splittable edges + } + else //allow splitting constraints + { + if (is_on_mesh(h) && is_on_mesh(hopp)) + return false; + else if (is_on_mesh(h) && is_on_border(hopp)) + return false; + else if (is_on_mesh(hopp) && is_on_border(h)) + return false; + else + return true; + } + } + + bool is_collapse_allowed(const edge_descriptor& e) const + { + halfedge_descriptor he = halfedge(e, mesh_); + halfedge_descriptor hopp = opposite(he, mesh_); + + if (!is_on_patch(he)) //hopp is also on patch + return false; + else if (is_on_patch_border(next(he, mesh_)) && is_on_patch_border(prev(he, mesh_))) + return false;//too many cases to be handled + else if (is_on_patch_border(next(hopp, mesh_)) && is_on_patch_border(prev(hopp, mesh_))) + return false;//too many cases to be handled + else if (is_on_patch_border(he) || is_on_patch_border(hopp)) + return !protect_constraints_;//allowed only when no protection + else + return true; + } + + bool collapse_does_not_invert_face(const halfedge_descriptor& h) const + { + vertex_descriptor vs = source(h, mesh_); + vertex_descriptor vt = target(h, mesh_); + + //backup source point + Point ps = get(vpmap_, vs); + //move source at target + put(vpmap_, vs, get(vpmap_, vt)); + + //collect normals to faces around vs AND vt + //vertices are at the same location, but connectivity is still be same, + //with plenty of degenerate triangles (which are common to both stars) + std::vector normals; + BOOST_FOREACH(halfedge_descriptor hd, + halfedges_around_target(h, mesh_)) + { + Vector_3 n = compute_normal(face(hd, mesh_)); + //n can be 0 in the splitting step because we remove degenerate faces + //only at the end of the splitting step + if(n != CGAL::NULL_VECTOR) + normals.push_back(n); + } + BOOST_FOREACH(halfedge_descriptor hd, + halfedges_around_target(opposite(h, mesh_), mesh_)) + { + Vector_3 n = compute_normal(face(hd, mesh_)); + if(n != CGAL::NULL_VECTOR) + normals.push_back(n); + } + + //check all normals have same orientation + bool res = true; + for(std::size_t i = 1; i < normals.size(); ++i)/*start at 1 on purpose*/ + { + if (normals[i-1] * normals[i] <= 0.) + { + res = false; + break; + } + } + //restore position + put(vpmap_, vs, ps); + return res; + } + + Vector_3 compute_normal(const face_descriptor& f) const + { + halfedge_descriptor hd = halfedge(f, mesh_); + typename GeomTraits::Triangle_3 + tr(get(vpmap_, target(hd, mesh_)), + get(vpmap_, target(next(hd, mesh_), mesh_)), + get(vpmap_, target(next(next(hd, mesh_), mesh_), mesh_))); + + if (tr.is_degenerate()) + return CGAL::NULL_VECTOR; + else + return PMP::compute_face_normal(f, mesh_); + } + + template + void tag_halfedges_status(const FaceRange& face_range + , const EdgeIsConstrainedMap& ecmap) + { + //tag MESH, //h and hopp belong to the mesh, not the patch + //tag MESH_BORDER //h belongs to the mesh, face(hopp, pmesh) == null_face() + BOOST_FOREACH(halfedge_descriptor h, halfedges(mesh_)) + { + //being part of the border of the mesh is predominant + if (is_border(h, mesh_)) + halfedge_status_map_[h] = MESH_BORDER; //erase previous value if exists + else + halfedge_status_map_[h] = MESH; + } + + //tag PATCH, //h and hopp belong to the patch to be remeshed + BOOST_FOREACH(face_descriptor f, face_range) + { + BOOST_FOREACH(halfedge_descriptor h, + halfedges_around_face(halfedge(f, mesh_), mesh_)) + { + halfedge_status_map_[h] = PATCH; + } + } + + //override the border of PATCH + //tag PATCH_BORDER,//h belongs to the patch, hopp doesn't + BOOST_FOREACH(edge_descriptor e, edges(mesh_)) + { + if (get(ecmap, e)) + { + //deal with h and hopp for borders that are sharp edges to be preserved + halfedge_descriptor h = halfedge(e, mesh_); + if (halfedge_status_map_[h] == PATCH) + halfedge_status_map_[h] = PATCH_BORDER; + + halfedge_descriptor hopp = opposite(h, mesh_); + if (halfedge_status_map_[hopp] == PATCH) + halfedge_status_map_[hopp] = PATCH_BORDER; + } + } + + CGAL_assertion(halfedge_status_map_.size() == nb_valid_halfedges()); + } + + Halfedge_status status(const halfedge_descriptor& h) const + { + typename boost::unordered_map < + halfedge_descriptor, Halfedge_status >::const_iterator + it = halfedge_status_map_.find(h); + CGAL_assertion(it != halfedge_status_map_.end()); + return it->second; + } + + void merge_status(const halfedge_descriptor& en, + const Halfedge_status& s_epo, + const Halfedge_status& s_ep) + { + CGAL_assertion(halfedge_status_map_.find(en) != halfedge_status_map_.end()); + + //get missing data + halfedge_descriptor eno = opposite(en, mesh_); + Halfedge_status s_eno = status(eno); + + if (s_epo == MESH_BORDER && s_eno == MESH_BORDER) + return; + + if(s_epo == MESH_BORDER + || s_ep == MESH_BORDER + || s_epo == PATCH_BORDER + || s_ep == PATCH_BORDER) + { + halfedge_status_map_[en] = s_epo; + halfedge_status_map_[eno] = s_ep; + } + // else keep current status for en and eno + } + + template + void fix_degenerate_faces(const vertex_descriptor& v, + Bimap& short_edges, + const double& sq_low) + { + CGAL_assertion_code(std::size_t nb_done = 0); + std::vector degenerate_faces; + BOOST_FOREACH(halfedge_descriptor h, + halfedges_around_target(halfedge(v, mesh_), mesh_)) + { + if (is_border(h, mesh_)) + continue; + if (PMP::is_degenerated(h, mesh_, vpmap_, GeomTraits())) + degenerate_faces.push_back(h); + } + while(!degenerate_faces.empty()) + { + halfedge_descriptor h = degenerate_faces.back(); + degenerate_faces.pop_back(); + + CGAL_assertion(PMP::is_degenerated(h, mesh_, vpmap_, GeomTraits())); + if (face(h, mesh_) == boost::graph_traits::null_face()) + continue; + + BOOST_FOREACH(halfedge_descriptor hf, + halfedges_around_face(h, mesh_)) + { + vertex_descriptor vc = target(hf, mesh_); + vertex_descriptor va = target(next(hf, mesh_), mesh_); + vertex_descriptor vb = target(next(next(hf, mesh_), mesh_), mesh_); + Vector_3 ab(get(vpmap_,va), get(vpmap_,vb)); + Vector_3 ac(get(vpmap_,va), get(vpmap_,vc)); + if (ab * ac < 0) + { + halfedge_descriptor hfo = opposite(hf, mesh_); + halfedge_descriptor h_ab = prev(hf, mesh_); + halfedge_descriptor h_ca = next(hf, mesh_); + + short_edges.left.erase(hf); + short_edges.left.erase(hfo); + + CGAL::Euler::flip_edge(hf, mesh_); + CGAL_assertion_code(++nb_done); + + //update halfedge_status_map_ + halfedge_status_map_[h_ab] = merge_status(h_ab, hf, hfo); + halfedge_status_map_[h_ca] = merge_status(h_ca, hf, hfo); + halfedge_status_map_[hf] = + (is_on_patch(h_ca) || is_on_patch_border(h_ca)) + ? PATCH + : MESH; + halfedge_status_map_[hfo] = status(hf); +#ifdef CGAL_PMP_REMESHING_DEBUG + debug_status_map(); +#endif + + //insert new edges in 'short_edges' + if (is_collapse_allowed(edge(hf, mesh_))) + { + double sqlen = sqlength(hf); + if (sqlen < sq_low) + short_edges.insert(typename Bimap::value_type(hf, sqlen)); + } + + if (!is_border(hf, mesh_) + && PMP::is_degenerated(hf, mesh_, vpmap_, GeomTraits())) + degenerate_faces.push_back(hf); + if (!is_border(hfo, mesh_) + && PMP::is_degenerated(hfo, mesh_, vpmap_, GeomTraits())) + degenerate_faces.push_back(hfo); + + break; + } + } + } +#ifdef CGAL_PMP_REMESHING_DEBUG + debug_status_map(); +#endif + } + + bool incident_to_degenerate(const halfedge_descriptor& he) + { + BOOST_FOREACH(halfedge_descriptor h, + halfedges_around_target(he, mesh_)) + { + if (is_border(h, mesh_)) + continue; + if (PMP::is_degenerated(h, mesh_, vpmap_, GeomTraits())) + return true; + } + return false; + } + + Halfedge_status merge_status(const halfedge_descriptor& h1, + const halfedge_descriptor& h2, + const halfedge_descriptor& h3) + { + Halfedge_status s1 = status(h1); + if (s1 == MESH_BORDER) return s1; + Halfedge_status s2 = status(h2); + if (s2 == MESH_BORDER) return s2; + Halfedge_status s3 = status(h3); + if (s3 == MESH_BORDER) return s3; + else if (s1 == PATCH_BORDER) return s1; + else if (s2 == PATCH_BORDER) return s2; + else if (s3 == PATCH_BORDER) return s3; + + CGAL_assertion(s1 == s2 && s1 == s3); + return s1; + } + + bool is_on_patch(const halfedge_descriptor& h) const + { + bool res =(status(h) == PATCH); + CGAL_assertion(res == (status(opposite(h, mesh_)) == PATCH)); + return res; + } + + bool is_on_patch(const face_descriptor& f) const + { + BOOST_FOREACH(halfedge_descriptor h, + halfedges_around_face(halfedge(f, mesh_), mesh_)) + { + if (is_on_patch(h) || is_on_patch_border(h)) + return true; + } + return false; + } + + bool is_on_patch(const vertex_descriptor& v) const + { + BOOST_FOREACH(halfedge_descriptor h, + halfedges_around_target(v, mesh_)) + { + if (!is_on_patch(h)) + return false; + } + return true; + } + + bool is_on_patch_border(const halfedge_descriptor& h) const + { + bool res = (status(h) == PATCH_BORDER); + if (res) + { + CGAL_assertion_code(Halfedge_status hs = status(opposite(h, mesh_))); + CGAL_assertion(hs == MESH_BORDER + || hs == MESH + || hs == PATCH_BORDER);//when 2 incident patches are remeshed + } + return res; + } + bool is_on_patch_border(const edge_descriptor& e) const + { + return is_on_patch_border(halfedge(e,mesh_)) + || is_on_patch_border(opposite(halfedge(e, mesh_), mesh_)); + } + bool is_on_patch_border(const vertex_descriptor& v) const + { + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, mesh_)) + { + if (is_on_patch_border(h) || is_on_patch_border(opposite(h, mesh_))) + return true; + } + return false; + } + + bool is_on_border(const halfedge_descriptor& h) const + { + bool res = (status(h) == MESH_BORDER); + CGAL_assertion(res == is_border(h, mesh_)); + CGAL_assertion(res == is_border(next(h, mesh_), mesh_)); + return res; + } + + bool is_on_border(const edge_descriptor& e) const + { + return is_on_border(halfedge(e, mesh_)) + || is_on_border(opposite(halfedge(e, mesh_), mesh_)); + } + + bool is_on_mesh(const halfedge_descriptor& h) const + { + return status(h) == MESH; + } + + void halfedge_added(const halfedge_descriptor& h, + const Halfedge_status& s) + { + halfedge_status_map_.insert(std::make_pair(h, s)); + } + + void halfedge_and_opp_removed(const halfedge_descriptor& h) + { + halfedge_status_map_.erase(h); + halfedge_status_map_.erase(opposite(h, mesh_)); + } + + std::size_t nb_valid_halfedges() const + { + return static_cast( + std::distance(halfedges(mesh_).first, halfedges(mesh_).second)); + } + + void debug_status_map() const + { + unsigned int nb_border = 0; + unsigned int nb_mesh = 0; + unsigned int nb_patch = 0; + unsigned int nb_patch_border = 0; + + typedef typename boost::unordered_map < + halfedge_descriptor, Halfedge_status>::value_type + HD_pair; + BOOST_FOREACH(const HD_pair& hs, halfedge_status_map_) + { + if(is_on_patch(hs.first)) nb_patch++; + else if(is_on_patch_border(hs.first)) nb_patch_border++; + else if(is_on_mesh(hs.first)) nb_mesh++; + else if(is_on_border(hs.first)) nb_border++; + else CGAL_assertion(false); + } + } + +#ifdef CGAL_PMP_REMESHING_DEBUG + void debug_self_intersections() const + { + std::cout << "Test self intersections..."; + std::vector > facets; + PMP::does_self_intersect( + mesh_, + PMP::parameters::vertex_point_map(vpmap_)); + CGAL_assertion(facets.empty()); + std::cout << "done." << std::endl; + } + + void debug_self_intersections(const vertex_descriptor& v) const + { + std::cout << "Test self intersections..."; + std::vector > facets; + PMP::does_self_intersect( + faces_around_target(halfedge(v, mesh_), mesh_), + mesh_, + std::back_inserter(facets), + PMP::parameters::vertex_point_map(vpmap_)); + CGAL_assertion(facets.empty()); + std::cout << "done." << std::endl; + } +#endif + + //warning : when v is on a sharp edge (angle <= 90 deg) + // which is not constrained (it's not mandatory) + //this test will return false, though normals are correct + bool check_normals(const vertex_descriptor& v) const + { + if (!is_on_patch(v)) + return true;//not much to say if we are on a boundary/sharp edge + + std::vector normals; + BOOST_FOREACH(halfedge_descriptor hd, + halfedges_around_target(halfedge(v, mesh_), mesh_)) + { + Vector_3 n = compute_normal(face(hd, mesh_)); + normals.push_back(n); + CGAL_assertion(n != CGAL::NULL_VECTOR); + } + //check all normals have same orientation + for (std::size_t i = 1; i < normals.size(); ++i)/*start at 1 on purpose*/ + { + double dot = normals[i - 1] * normals[i]; + if(dot <= 0.) + return false; + } + return true; + } + + bool check_normals(const halfedge_descriptor& h) const + { + if (!is_on_patch(h)) + return true;//nothing to say + Vector_3 n = compute_normal(face(h, mesh_)); + Vector_3 no = compute_normal(face(opposite(h, mesh_), mesh_)); + return n * no > 0.; + } + + private: + PolygonMesh& mesh_; + VertexPointMap& vpmap_; + const AABB_tree* tree_ptr_; + bool own_tree_; + Triangle_list input_triangles_; + boost::unordered_map halfedge_status_map_; + bool protect_constraints_; + + };//end class Incremental_remesher +}//end namespace internal +}//end namespace Polygon_mesh_processing +}//end namespace CGAL + +#endif //CGAL_POLYGON_MESH_PROCESSING_REMESH_IMPL_H diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h new file mode 100644 index 00000000000..2bcf059c0a1 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -0,0 +1,269 @@ +// Copyright (c) 2015 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// 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. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// +// Author(s) : Jane Tournois + +#ifndef CGAL_POLYGON_MESH_PROCESSING_REMESH_H +#define CGAL_POLYGON_MESH_PROCESSING_REMESH_H + +#include + +#include +#include + +namespace CGAL { + +namespace Polygon_mesh_processing { + +/*! +* \ingroup remeshing_grp +* @brief remeshes a triangulated region of a polygon mesh. +* This operation sequentially performs edge splits, edge collapses, +* edge flips, Laplacian smoothing and projection to the initial surface +* to generate a smooth mesh with a prescribed edge length. +* +* @tparam PolygonMesh model of `MutableFaceGraph` that +* has an internal property map for `CGAL::vertex_point_t`. +* @tparam FaceRange range of `boost::graph_traits::%face_descriptor`, + model of `Range`. Its iterator type is `InputIterator`. +* @tparam NamedParameters a sequence of \ref namedparameters +* +* @param pmesh a polygon mesh with triangulated surface patches to be remeshed +* @param faces the range of triangular faces defining one or several surface patches to be remeshed +* @param target_edge_length the edge length that is targetted in the remeshed patch +* @param np optional sequence of \ref namedparameters among the ones listed below +* +* @pre if constraints protection is activated, the constrained edges should +* not be longer than 4/3*`target_edge_length` +* +* \cgalNamedParamsBegin +* \cgalParamBegin{vertex_point_map} the property map with the points associated +* to the vertices of `pmesh`. Instance of a class model of `ReadWritePropertyMap`. +* \cgalParamEnd +* \cgalParamBegin{number_of_iterations} the number of iterations for the +* sequence of atomic operations performed (listed in the above description) +* \cgalParamEnd +* \cgalParamBegin{geom_traits} a geometric traits class instance, model of `Kernel` +* \cgalParamEnd +* \cgalParamBegin{edge_is_constrained_map} a property map containing the +* constrained-or-not status of each edge of pmesh. A constrained edge can be splitted +* or collapsed, but not flipped, nor its endpoints moved by smoothing. +* Note that patch boundary edges (i.e. incident to only one face in the range) +* are always considered as constrained edges. +* \cgalParamEnd +* \cgalParamBegin{protect_constraints} If `true`, the edges set as constrained +* in `edge_is_constrained_map` (or by default the boundary edges) +* are not splitted nor collapsed during remeshing. +* Note that around constrained edges that have their length higher than +* twice `target_edge_length`, remeshing will fail to provide +* good quality results. It can even fail to terminate because of cascading vertex +* insertions. +* \cgalParamEnd +* \cgalNamedParamsEnd +* +* @sa `split_long_edges()` +* +*@todo add possibility to provide a functor that projects to a prescribed surface +*/ +template +void isotropic_remeshing(PolygonMesh& pmesh + , const FaceRange& faces + , const double& target_edge_length + , const NamedParameters& np) +{ + typedef PolygonMesh PM; + using boost::choose_pmap; + using boost::get_param; + using boost::choose_param; + + typedef typename GetGeomTraits::type GT; + + typedef typename GetVertexPointMap::type VPMap; + VPMap vpmap = choose_pmap(get_param(np, boost::vertex_point), + pmesh, + boost::vertex_point); + + typedef typename boost::lookup_named_param_def < + CGAL::edge_is_constrained_t, + NamedParameters, + internal::Border_constraint_pmap//default + > ::type ECMap; + ECMap ecmap + = choose_param(get_param(np, edge_is_constrained), + internal::Border_constraint_pmap(pmesh, faces)); + + double low = 4. / 5. * target_edge_length; + double high = 4. / 3. * target_edge_length; + + bool protect = choose_param(get_param(np, protect_constraints), false); + if(protect) + { + std::string msg("Isotropic remeshing : protect_constraints cannot be set to"); + msg.append(" true with constraints larger than 4/3 * target_edge_length."); + msg.append(" Remeshing aborted."); + CGAL_precondition_msg( + internal::constraints_are_short_enough(pmesh, ecmap, vpmap, high), + msg.c_str()); + } + + typename internal::Incremental_remesher + remesher(pmesh, vpmap, protect); + remesher.init_faces_remeshing(faces, ecmap); + + unsigned int nb_iterations = choose_param(get_param(np, number_of_iterations), 1); + +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << std::endl; + std::cout << "Remeshing (size = " << target_edge_length; + std::cout << ", #iter = " << nb_iterations << ")..." << std::endl; +#endif + + for (unsigned int i = 0; i < nb_iterations; ++i) + { +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << " * Iteration " << (i + 1) << " *" << std::endl; +#endif + + remesher.split_long_edges(high); + remesher.collapse_short_edges(low, high); + remesher.equalize_valences(); + remesher.tangential_relaxation(); + remesher.project_to_surface(); + +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << std::endl; +#endif + } + +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "Remeshing done (size = " << target_edge_length; + std::cout << ", #iter = " << nb_iterations << ")." << std::endl; +#endif +} + +template +void isotropic_remeshing(PolygonMesh& pmesh + , const FaceRange& faces + , const double& target_edge_length) +{ + isotropic_remeshing(pmesh, + faces, + target_edge_length, + parameters::all_default()); +} + +/*! +* \ingroup remeshing_grp +* @brief splits the edges listed in `edges` into sub-edges +* that are not longer than the given threshold `max_length`. +* +* Note this function is useful to split constrained edges before +* calling `isotropic_remeshing()` with protection of constraints +* activated (to match the constrained edge length required by the +* remeshing algorithm to be guaranteed to terminate) +* +* @tparam PolygonMesh model of `MutableFaceGraph` that +* has an internal property map for `CGAL::vertex_point_t`. +* @tparam EdgeRange range of `boost::graph_traits::%edge_descriptor`, +* model of `Range`. Its iterator type is `InputIterator`. +* @tparam NamedParameters a sequence of \ref namedparameters +* +* @param pmesh a polygon mesh +* @param edges the range of edges to be split if they are longer than given threshold +* @param max_length the edge length above which an edge from `edges` is split +* into to sub-edges +* @param np optional \ref namedparameters described below + +* \cgalNamedParamsBegin +* \cgalParamBegin{vertex_point_map} the property map with the points associated +* to the vertices of `pmesh`. Instance of a class model of `ReadWritePropertyMap`. +* \cgalParamEnd +* \cgalNamedParamsEnd +* +* @sa `isotropic_remeshing()` +* +*/ +template +void split_long_edges(PolygonMesh& pmesh + , EdgeRange& edges + , const double& max_length + , const NamedParameters& np) +{ + typedef PolygonMesh PM; + using boost::choose_pmap; + using boost::get_param; + + typedef typename GetGeomTraits::type GT; + typedef typename GetVertexPointMap::type VPMap; + VPMap vpmap = choose_pmap(get_param(np, boost::vertex_point), + pmesh, + boost::vertex_point); + + typename internal::Incremental_remesher + remesher(pmesh, vpmap, false/*protect constraints*/); + + remesher.split_long_edges(edges, max_length, Emptyset_iterator()); +} + +template +void split_long_edges(PolygonMesh& pmesh + , EdgeRange& edges + , const double& max_length) +{ + split_long_edges(pmesh, + edges, + max_length, + parameters::all_default()); +} + +//used in the Polyhedron demo +template +void split_long_edges(PolygonMesh& pmesh + , EdgeRange& edge_range + , const double& max_length + , OutputIterator out//edges after splitting, all shorter than target_length + , const NamedParameters& np) +{ + typedef PolygonMesh PM; + using boost::choose_pmap; + using boost::get_param; + + typedef typename GetGeomTraits::type GT; + typedef typename GetVertexPointMap::type VPMap; + VPMap vpmap = choose_pmap(get_param(np, boost::vertex_point), + pmesh, + boost::vertex_point); + + typename internal::Incremental_remesher + remesher(pmesh, vpmap, false/*protect constraints*/); + + remesher.split_long_edges(edge_range, max_length, out); +} + +} //end namespace Polygon_mesh_processing +} //end namespace CGAL + +#endif diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index 0beb2704097..564a6307cad 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -124,6 +124,8 @@ bool is_degenerated( const VertexPointMap& vpmap, const Traits& traits) { + CGAL_assertion(!is_border(hd, tmesh)); + const typename Traits::Point_3& p1 = get(vpmap, target( hd, tmesh) ); const typename Traits::Point_3& p2 = get(vpmap, target(next(hd, tmesh), tmesh) ); const typename Traits::Point_3& p3 = get(vpmap, source( hd, tmesh) ); @@ -806,6 +808,40 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh) } /// \endcond + +#ifdef PMP_TODO_DOC +/// \ingroup PkgPolygonMeshProcessing +/// removes the isolated vertices from any polygon mesh. +/// A vertex is considered isolated if it is not incident to any simplex +/// of higher dimension. +/// +/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// +/// @param pmesh the polygon mesh to be repaired +/// +/// @return number of removed isolated vertices +/// +#endif +template +std::size_t remove_isolated_vertices(PolygonMesh& pmesh) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + std::vector to_be_removed; + + BOOST_FOREACH(vertex_descriptor v, vertices(pmesh)) + { + if (CGAL::halfedges_around_target(v, pmesh).first + == CGAL::halfedges_around_target(v, pmesh).second) + to_be_removed.push_back(v); + } + std::size_t nb_removed = to_be_removed.size(); + BOOST_FOREACH(vertex_descriptor v, to_be_removed) + { + remove_vertex(v, pmesh); + } + return nb_removed; +} + } } // end of CGAL::Polygon_mesh_processing #endif // CGAL_POLYGON_MESH_PROCESSING_REPAIR_H diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index ce0b8b564fe..d17941b0113 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -76,6 +76,7 @@ if (EIGEN3_FOUND) create_single_source_cgal_program("test_stitching.cpp") create_single_source_cgal_program("remove_degeneracies_test.cpp") create_single_source_cgal_program("test_pmp_bgl_named_params.cpp") + create_single_source_cgal_program("remeshing_test.cpp" ) if(NOT (${EIGEN3_VERSION} VERSION_LESS 3.2.0)) create_single_source_cgal_program("fairing_test.cpp") diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/joint-patch-toolargeconstraints.selection.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/joint-patch-toolargeconstraints.selection.txt new file mode 100644 index 00000000000..948cc4441b2 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/joint-patch-toolargeconstraints.selection.txt @@ -0,0 +1,3 @@ + +576 574 570 568 572 566 564 562 500 498 496 494 462 460 448 165 164 163 162 154 153 +913 910 905 903 899 893 887 873 884 867 861 858 855 853 851 848 845 842 839 833 827 821 819 810 807 798 795 792 789 783 780 881 774 830 771 765 762 748 743 741 738 735 732 729 727 724 721 718 712 706 703 700 697 694 691 688 686 682 677 653 650 640 634 801 622 610 606 600 514 511 509 507 503 501 499 497 491 759 489 487 485 480 478 777 472 470 756 466 464 459 457 455 451 449 505 447 804 445 438 430 427 426 423 646 421 419 414 824 408 404 402 400 398 619 396 394 391 389 387 517 385 383 379 377 432 375 373 371 474 369 365 363 361 359 495 357 355 353 468 350 346 344 341 381 337 333 483 332 330 327 324 321 751 319 318 313 312 348 305 302 443 412 301 299 297 864 291 289 286 786 284 417 282 280 277 316 275 493 271 267 255 252 250 244 434 242 513 453 240 462 238 236 234 231 226 224 896 261 217 195 204 193 176 288 169 165 410 161 156 146 144 878 142 140 135 133 715 131 642 406 129 870 608 294 127 125 123 121 263 117 115 113 111 109 107 105 103 101 248 99 97 93 89 875 87 367 85 813 83 81 311 269 119 79 77 709 75 73 71 65 63 246 61 59 57 440 67 55 53 95 51 49 47 45 43 41 39 37 753 35 890 32 30 28 745 25 816 768 23 21 138 19 436 259 17 15 91 13 11 9 7 5 476 69 3 836 0 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/joint-patch.selection.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/joint-patch.selection.txt new file mode 100644 index 00000000000..9df43e3290f --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/joint-patch.selection.txt @@ -0,0 +1,3 @@ + +608 604 602 596 594 593 600 591 587 581 579 577 585 575 571 569 567 583 563 512 510 509 589 565 503 573 501 507 505 499 497 470 468 467 465 606 461 451 449 516 514 495 463 168 598 167 +913 905 903 899 893 890 884 875 867 861 848 845 842 833 830 827 824 819 816 813 807 804 801 798 795 792 789 786 783 777 774 771 768 765 762 759 756 753 748 745 743 741 738 729 724 703 700 688 677 653 646 642 634 709 619 650 610 600 715 517 514 513 511 509 896 507 503 499 495 735 493 491 489 751 487 483 480 478 476 472 466 464 697 474 462 459 457 810 455 453 449 694 447 438 434 432 427 878 423 430 421 436 419 417 414 836 412 732 404 402 400 398 396 851 394 391 389 387 385 721 383 910 691 381 379 377 375 712 373 371 363 361 359 355 353 348 346 485 344 686 341 337 333 332 330 327 324 319 682 318 316 313 312 311 858 302 887 299 780 291 369 289 321 297 288 286 284 497 280 470 277 410 275 367 271 269 267 853 263 261 259 255 864 252 248 246 244 242 240 357 234 226 224 217 204 195 440 176 169 165 282 161 156 146 144 640 406 140 138 135 718 445 294 133 131 873 129 250 127 125 123 727 121 236 119 117 238 115 111 109 107 105 365 103 101 99 193 97 95 93 91 87 85 83 608 305 81 426 79 501 77 75 821 73 443 71 69 67 622 65 468 63 61 59 706 57 55 505 53 51 49 47 881 231 45 451 43 142 41 39 37 113 35 89 32 855 30 606 301 28 350 25 839 23 870 408 21 19 17 15 13 11 9 7 5 3 0 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/joint_refined.off b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/joint_refined.off new file mode 100644 index 00000000000..8f0907a1c0e --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/joint_refined.off @@ -0,0 +1,917 @@ +OFF +303 610 0 + +0.18987 0.5 0.0145757 +0.18987 -0.5 0.0145757 +0.160511 -0.5 0.0325677 +0.160511 0.5 0.0325677 +0.128697 -0.5 0.0457447 +0.128697 0.5 0.0457447 +0.0952137 -0.5 0.0537838 +0.0952137 0.5 0.0537838 +0.0608858 -0.5 0.0564848 +0.0608858 0.5 0.0564848 +0.0265575 -0.5 0.0537838 +0.0265575 0.5 0.0537838 +-0.0069256 -0.5 0.0457447 +-0.0069256 0.5 0.0457447 +-0.038739 -0.5 0.0325677 +-0.038739 0.5 0.0325677 +-0.0680987 -0.5 0.0145757 +-0.0680987 0.5 0.0145757 +-0.0942827 -0.5 -0.00778743 +-0.0942827 0.5 -0.00778743 +-0.116646 -0.5 -0.0339725 +-0.116646 0.5 -0.0339725 +-0.134639 -0.5 -0.0633324 +-0.134639 0.5 -0.0633324 +-0.147815 -0.5 -0.0951457 +-0.147815 0.5 -0.0951457 +-0.155855 -0.5 -0.128628 +-0.155855 0.5 -0.128628 +-0.158556 -0.5 -0.162956 +-0.158556 0.5 -0.162956 +-0.155855 -0.5 -0.197285 +-0.155855 0.5 -0.197285 +-0.147815 -0.5 -0.230768 +-0.147815 0.5 -0.230768 +-0.134639 -0.5 -0.262581 +-0.134639 0.5 -0.262581 +-0.116646 -0.5 -0.291941 +-0.116646 0.5 -0.291941 +-0.0942827 -0.5 -0.318125 +-0.0942827 0.5 -0.318125 +-0.0680987 -0.5 -0.340488 +-0.0680987 0.5 -0.340488 +-0.038739 -0.5 -0.358481 +-0.038739 0.5 -0.358481 +-0.0069256 -0.5 -0.371658 +-0.0069256 0.5 -0.371658 +0.0265574 -0.5 -0.379697 +0.0265574 0.5 -0.379697 +0.0608857 -0.5 -0.382398 +0.0608857 0.5 -0.382398 +0.0952137 -0.5 -0.379697 +0.0952137 0.5 -0.379697 +0.128696 -0.5 -0.371658 +0.128696 0.5 -0.371658 +0.16051 -0.5 -0.358481 +0.16051 0.5 -0.358481 +0.18987 -0.5 -0.340488 +0.18987 0.5 -0.340488 +0.216054 -0.5 -0.318125 +0.216054 0.5 -0.318125 +0.238418 -0.5 -0.291941 +0.238418 0.5 -0.291941 +0.25641 -0.5 -0.262581 +0.25641 0.5 -0.262581 +0.269587 -0.5 -0.230768 +0.269587 0.5 -0.230768 +0.277626 -0.5 -0.197285 +0.277626 0.5 -0.197285 +0.280327 -0.5 -0.162956 +0.280327 0.5 -0.162956 +0.277626 -0.5 -0.128628 +0.277626 0.5 -0.128628 +0.269587 -0.5 -0.0951457 +0.269587 0.5 -0.0951457 +0.25641 -0.5 -0.0633324 +0.25641 0.5 -0.0633324 +0.238418 -0.5 -0.0339725 +0.238418 0.5 -0.0339725 +0.216054 -0.5 -0.00778851 +0.216054 0.5 -0.00778851 +0.157965 -0.5 -0.461734 +0.203507 -0.5 -0.442869 +0.24554 -0.5 -0.417111 +0.283026 -0.5 -0.385097 +0.315041 -0.5 -0.347611 +0.340798 -0.5 -0.305579 +0.359662 -0.5 -0.260035 +0.371171 -0.5 -0.212101 +0.375039 -0.5 -0.162956 +0.371171 -0.5 -0.113812 +0.359662 -0.5 -0.0658783 +0.340799 -0.5 -0.0203345 +0.315041 -0.5 0.0216976 +0.283026 -0.5 0.0591838 +0.245541 -0.5 0.0911989 +0.203508 -0.5 0.116956 +0.157965 -0.5 0.13582 +0.11003 -0.5 0.147329 +0.0608858 -0.5 0.151196 +-0.375039 -0.5 -0.162956 +0.0608858 -0.5 -0.47711 +0.11003 -0.5 -0.473241 +0.203508 0.5 0.116956 +0.245541 0.5 0.0911989 +0.283026 0.5 0.0591838 +0.315041 0.5 0.0216986 +0.340799 0.5 -0.0203345 +0.359662 0.5 -0.0658783 +0.371171 0.5 -0.113812 +0.375039 0.5 -0.162956 +0.371171 0.5 -0.212101 +0.359662 0.5 -0.260035 +0.340798 0.5 -0.305579 +0.315041 0.5 -0.347611 +0.283026 0.5 -0.385097 +0.24554 0.5 -0.417111 +0.203507 0.5 -0.442869 +0.157965 0.5 -0.461734 +0.11003 0.5 -0.473241 +0.0608858 0.5 -0.47711 +-0.375039 0.5 -0.162956 +0.0608858 0.5 0.151196 +0.11003 0.5 0.147329 +0.157965 0.5 0.13582 +-0.375039 -0.5 -0.47711 +-0.157078 -0.5 0.151196 +-0.375039 -0.5 0.151196 +-0.375039 0.5 -0.47711 +-0.375039 -0.185847 0.47711 +-0.157078 -0.185847 0.47711 +-0.157078 0.5 0.47711 +-0.375039 0.5 0.47711 +-0.375039 0.5 0.151196 +-0.157078 0.5 0.151196 +-0.375039 -0.185847 0.151196 +-0.375039 -0.5 0.47711 +-0.157078 -0.5 0.47711 +0.0608859 -0.5 0.47711 +0.0608859 -0.185847 0.47711 +-0.157078 -0.185847 0.151196 +0.0608858 -0.185847 0.151196 +-0.375039 -0.315312 0.400904 +-0.375039 -0.329311 0.404264 +-0.375039 -0.343663 0.405393 +-0.375039 -0.358016 0.404264 +-0.375039 -0.372015 0.400904 +-0.375039 -0.385316 0.395395 +-0.375039 -0.397591 0.387872 +-0.375039 -0.408538 0.378521 +-0.375039 -0.417888 0.367575 +-0.375039 -0.42541 0.355299 +-0.375039 -0.43092 0.341999 +-0.375039 -0.434281 0.328 +-0.375039 -0.43541 0.313648 +-0.375039 -0.434281 0.299295 +-0.375039 -0.43092 0.285296 +-0.375039 -0.42541 0.271996 +-0.375039 -0.417888 0.25972 +-0.375039 -0.408538 0.248773 +-0.375039 -0.397591 0.239422 +-0.375039 -0.385316 0.2319 +-0.375039 -0.372015 0.22639 +-0.375039 -0.358016 0.223031 +-0.375039 -0.343663 0.221901 +-0.375039 -0.329311 0.223031 +-0.375039 -0.315312 0.22639 +-0.375039 -0.302011 0.2319 +-0.375039 -0.289736 0.239422 +-0.375039 -0.278789 0.248773 +-0.375039 -0.269439 0.25972 +-0.375039 -0.261916 0.271996 +-0.375039 -0.256407 0.285296 +-0.375039 -0.253046 0.299295 +-0.375039 -0.251917 0.313648 +-0.375039 -0.253046 0.328 +-0.375039 -0.256407 0.341999 +-0.375039 -0.261916 0.355299 +-0.375039 -0.269439 0.367575 +-0.375039 -0.278789 0.378521 +-0.375039 -0.289736 0.387872 +-0.375039 -0.302011 0.395395 +0.0608859 -0.302011 0.395395 +0.0608859 -0.289736 0.387872 +0.0608859 -0.278789 0.378521 +0.0608859 -0.269439 0.367575 +0.0608859 -0.261916 0.355299 +0.0608859 -0.256407 0.341999 +0.0608859 -0.253046 0.328 +0.0608859 -0.251917 0.313648 +0.0608859 -0.253046 0.299295 +0.0608859 -0.256407 0.285296 +0.0608859 -0.261916 0.271996 +0.0608859 -0.269439 0.25972 +0.0608859 -0.278789 0.248773 +0.0608859 -0.289736 0.239422 +0.0608859 -0.302011 0.2319 +0.0608859 -0.315312 0.22639 +0.0608859 -0.329311 0.223031 +0.0608859 -0.343663 0.221901 +0.0608859 -0.358016 0.223031 +0.0608859 -0.372015 0.22639 +0.0608859 -0.385316 0.2319 +0.0608859 -0.397591 0.239422 +0.0608859 -0.408538 0.248773 +0.0608859 -0.417888 0.25972 +0.0608859 -0.42541 0.271996 +0.0608859 -0.43092 0.285296 +0.0608859 -0.434281 0.299295 +0.0608859 -0.43541 0.313648 +0.0608859 -0.434281 0.328 +0.0608859 -0.43092 0.341999 +0.0608859 -0.42541 0.355299 +0.0608859 -0.417888 0.367575 +0.0608859 -0.408538 0.378521 +0.0608859 -0.397591 0.387872 +0.0608859 -0.385316 0.395395 +0.0608859 -0.372015 0.400904 +0.0608859 -0.358016 0.404264 +0.0608859 -0.343663 0.405393 +0.0608859 -0.329311 0.404264 +0.0608859 -0.315312 0.400904 +-0.375039 0 -0.47711 +-0.157078 0.1570765 0.47711 +-0.157078 0.1570765 0.151196 +-0.375039 -0.25 -0.47711 +-0.375039 0.25 -0.47711 +-0.1570766 -0.5 -0.47711 +-0.1570766 0.5 -0.47711 +-0.157078 -0.01438525 0.47711 +-0.157078 0.32853825 0.47711 +-0.157078 0.32853825 0.151196 +-0.157078 -0.01438525 0.151196 +-0.157078 0.5 0.314153 +-0.157078 -0.185847 0.314153 +0.0608858 -0.3429235 0.151196 +-0.375039 -0.375 -0.47711 +-0.375039 -0.125 -0.47711 +-0.375039 0.125 -0.47711 +-0.375039 0.375 -0.47711 +-0.0480961 -0.185847 0.151196 +-0.0480961 0.5 0.151196 +-0.0480954 -0.5 -0.47711 +-0.2660578 -0.5 -0.47711 +-0.2660578 0.5 -0.47711 +-0.0480954 0.5 -0.47711 +-0.157078 -0.100116125 0.47711 +-0.157078 0.071345625 0.47711 +-0.157078 0.242807375 0.47711 +-0.157078 0.414269125 0.47711 +-0.157078 0.414269125 0.151196 +-0.157078 0.242807375 0.151196 +-0.157078 0.071345625 0.151196 +-0.157078 -0.100116125 0.151196 +-0.157078 0.5 0.2326745 +-0.157078 0.5 0.3956315 +-0.157078 -0.185847 0.3956315 +-0.157078 -0.185847 0.2326745 +0.0608858 -0.42146175 0.151196 +0.0608858 -0.26438525 0.151196 +-0.375039 -0.4375 -0.47711 +-0.375039 -0.3125 -0.47711 +-0.375039 -0.1875 -0.47711 +-0.375039 -0.0625 -0.47711 +-0.375039 0.0625 -0.47711 +-0.375039 0.1875 -0.47711 +-0.375039 0.3125 -0.47711 +-0.375039 0.4375 -0.47711 +0.00639485 -0.185847 0.151196 +-0.10258705 -0.185847 0.151196 +-0.10258705 0.5 0.151196 +0.00639485 0.5 0.151196 +0.0063952 -0.5 -0.47711 +-0.102586 -0.5 -0.47711 +-0.2115672 -0.5 -0.47711 +-0.3205484 -0.5 -0.47711 +-0.3205484 0.5 -0.47711 +-0.2115672 0.5 -0.47711 +-0.102586 0.5 -0.47711 +0.0063952 0.5 -0.47711 +-0.157078 -0.1429815625 0.47711 +-0.157078 -0.0572506875 0.47711 +-0.157078 0.0284801875 0.47711 +-0.157078 0.1142110625 0.47711 +-0.157078 0.1999419375 0.47711 +-0.157078 0.2856728125 0.47711 +-0.157078 0.3714036875 0.47711 +-0.157078 0.4571345625 0.47711 +-0.157078 0.4571345625 0.151196 +-0.157078 0.3714036875 0.151196 +-0.157078 0.2856728125 0.151196 +-0.157078 0.1999419375 0.151196 +-0.157078 0.1142110625 0.151196 +-0.157078 0.0284801875 0.151196 +-0.157078 -0.0572506875 0.151196 +-0.157078 -0.1429815625 0.151196 +-0.157078 0.5 0.19193525 +-0.157078 0.5 0.27341375 +-0.157078 0.5 0.35489225 +-0.157078 0.5 0.43637075 +-0.157078 -0.185847 0.43637075 +-0.157078 -0.185847 0.35489225 +-0.157078 -0.185847 0.27341375 +-0.157078 -0.185847 0.19193525 +3 3 0 1 +3 3 1 2 +3 5 3 2 +3 5 2 4 +3 7 5 4 +3 7 4 6 +3 9 7 6 +3 9 6 8 +3 11 9 8 +3 11 8 10 +3 13 11 10 +3 13 10 12 +3 15 13 12 +3 17 14 16 +3 19 17 16 +3 23 21 20 +3 23 20 22 +3 25 23 22 +3 25 22 24 +3 27 25 24 +3 27 24 26 +3 29 27 26 +3 29 26 28 +3 31 29 28 +3 31 28 30 +3 33 31 30 +3 33 30 32 +3 35 33 32 +3 35 32 34 +3 37 35 34 +3 37 34 36 +3 39 37 36 +3 39 36 38 +3 41 39 38 +3 41 38 40 +3 43 41 40 +3 43 40 42 +3 45 43 42 +3 45 42 44 +3 47 45 44 +3 47 44 46 +3 49 47 46 +3 49 46 48 +3 51 49 48 +3 51 48 50 +3 53 51 50 +3 53 50 52 +3 55 53 52 +3 55 52 54 +3 57 55 54 +3 57 54 56 +3 59 57 56 +3 59 56 58 +3 61 59 58 +3 61 58 60 +3 63 61 60 +3 63 60 62 +3 65 63 62 +3 65 62 64 +3 67 65 64 +3 67 64 66 +3 69 67 66 +3 69 66 68 +3 71 69 68 +3 71 68 70 +3 73 71 70 +3 77 75 74 +3 77 74 76 +3 79 77 76 +3 79 76 78 +3 0 79 78 +3 0 78 1 +3 99 28 26 +3 30 28 99 +3 99 26 24 +3 32 30 99 +3 99 24 22 +3 34 32 99 +3 80 52 101 +3 83 60 58 +3 90 70 89 +3 99 22 20 +3 36 34 99 +3 20 126 99 +3 124 38 36 +3 10 97 98 +3 97 8 6 +3 97 6 4 +3 96 2 95 +3 89 70 68 +3 87 66 64 +3 101 52 50 +3 101 50 48 +3 100 48 46 +3 120 29 31 +3 27 29 120 +3 120 31 33 +3 25 27 120 +3 120 33 35 +3 23 25 120 +3 123 95 102 +3 55 115 116 +3 120 35 37 +3 103 79 0 +3 113 59 61 +3 37 39 127 +3 19 21 120 +3 118 49 51 +3 118 51 53 +3 118 53 55 +3 111 61 63 +3 111 63 65 +3 123 0 3 +3 123 3 5 +3 122 123 5 +3 122 5 7 +3 122 7 9 +3 122 9 11 +3 122 11 13 +3 13 133 269 +3 15 17 133 +3 133 17 19 +3 88 109 108 +3 88 108 89 +3 87 110 109 +3 87 109 88 +3 86 111 110 +3 86 110 87 +3 85 112 111 +3 85 111 86 +3 84 113 112 +3 84 112 85 +3 83 113 84 +3 82 114 83 +3 81 116 115 +3 81 115 82 +3 80 116 81 +3 101 118 117 +3 101 117 80 +3 100 119 118 +3 100 118 101 +3 97 122 121 +3 96 123 122 +3 96 122 97 +3 95 123 96 +3 92 105 104 +3 92 104 93 +3 91 106 105 +3 91 105 92 +3 90 107 106 +3 90 106 91 +3 89 108 107 +3 89 107 90 +3 131 128 129 +3 131 129 279 +3 119 124 259 +3 119 100 271 +3 120 127 266 +3 120 124 99 +3 120 99 126 +3 120 126 134 +3 120 134 132 +3 128 135 136 +3 128 136 129 +3 129 136 137 +3 129 137 138 +3 230 121 270 +3 231 129 299 +3 229 133 295 +3 132 130 298 +3 130 132 131 +3 131 132 134 +3 172 128 131 +3 135 126 125 +3 135 125 136 +3 167 168 134 +3 134 168 169 +3 166 167 134 +3 134 169 170 +3 165 166 134 +3 165 134 126 +3 164 165 126 +3 163 164 126 +3 162 163 126 +3 161 162 126 +3 160 161 126 +3 159 160 126 +3 158 159 126 +3 157 158 126 +3 156 157 126 +3 134 170 171 +3 134 171 172 +3 128 172 173 +3 128 173 174 +3 128 174 175 +3 128 175 176 +3 128 176 177 +3 128 177 178 +3 128 178 179 +3 128 179 180 +3 128 180 141 +3 135 128 141 +3 135 141 142 +3 135 142 143 +3 135 143 144 +3 135 144 145 +3 135 145 146 +3 135 146 147 +3 135 147 148 +3 135 148 149 +3 135 149 150 +3 135 150 151 +3 155 156 126 +3 155 126 135 +3 154 155 135 +3 153 154 135 +3 152 153 135 +3 135 151 152 +3 136 125 98 +3 136 98 137 +3 202 203 98 +3 98 203 204 +3 201 202 98 +3 98 204 205 +3 200 201 98 +3 200 98 257 +3 199 200 140 +3 198 199 140 +3 197 198 140 +3 196 197 140 +3 195 196 140 +3 194 195 140 +3 193 194 140 +3 192 193 140 +3 191 192 140 +3 98 205 206 +3 137 98 206 +3 137 206 207 +3 137 207 208 +3 137 208 209 +3 137 209 210 +3 137 210 211 +3 137 211 212 +3 137 212 213 +3 137 213 214 +3 137 214 215 +3 137 215 216 +3 138 137 216 +3 138 216 217 +3 138 217 218 +3 138 218 219 +3 138 219 220 +3 138 220 181 +3 138 181 182 +3 138 182 183 +3 138 183 184 +3 138 184 185 +3 138 185 186 +3 190 191 140 +3 190 140 138 +3 189 190 138 +3 188 189 138 +3 187 188 138 +3 138 186 187 +3 138 140 267 +3 138 139 302 +3 184 183 178 +3 184 178 177 +3 183 182 179 +3 183 179 178 +3 182 181 180 +3 182 180 179 +3 181 220 141 +3 181 141 180 +3 220 219 142 +3 220 142 141 +3 219 218 143 +3 219 143 142 +3 218 217 144 +3 218 144 143 +3 217 216 145 +3 217 145 144 +3 216 215 146 +3 216 146 145 +3 215 214 147 +3 215 147 146 +3 214 213 148 +3 214 148 147 +3 213 212 149 +3 213 149 148 +3 212 211 150 +3 212 150 149 +3 211 210 151 +3 211 151 150 +3 210 209 152 +3 210 152 151 +3 209 208 153 +3 209 153 152 +3 208 207 154 +3 208 154 153 +3 207 206 155 +3 207 155 154 +3 206 205 156 +3 206 156 155 +3 205 204 157 +3 205 157 156 +3 204 203 158 +3 204 158 157 +3 203 202 159 +3 203 159 158 +3 202 201 160 +3 202 160 159 +3 201 200 161 +3 201 161 160 +3 200 199 162 +3 200 162 161 +3 199 198 163 +3 199 163 162 +3 198 197 164 +3 198 164 163 +3 197 196 165 +3 197 165 164 +3 196 195 166 +3 196 166 165 +3 195 194 167 +3 195 167 166 +3 194 193 168 +3 194 168 167 +3 193 192 169 +3 193 169 168 +3 192 191 170 +3 192 170 169 +3 191 190 171 +3 191 171 170 +3 190 189 172 +3 190 172 171 +3 189 188 173 +3 189 173 172 +3 188 187 174 +3 188 174 173 +3 187 186 175 +3 187 175 174 +3 186 185 176 +3 186 176 175 +3 185 184 177 +3 185 177 176 +3 121 139 268 +3 97 121 140 +3 115 55 57 +3 115 57 59 +3 113 83 114 +3 59 113 114 +3 114 82 115 +3 114 115 59 +3 117 118 55 +3 116 80 117 +3 116 117 55 +3 132 133 19 +3 127 41 43 +3 39 41 127 +3 105 75 77 +3 105 77 79 +3 103 93 104 +3 79 103 104 +3 104 105 79 +3 106 73 75 +3 65 67 110 +3 61 111 112 +3 112 113 61 +3 110 67 69 +3 69 71 109 +3 109 110 69 +3 108 109 71 +3 71 73 107 +3 106 107 73 +3 75 105 106 +3 13 15 133 +3 121 122 13 +3 102 103 0 +3 0 123 102 +3 45 47 119 +3 49 118 119 +3 47 49 119 +3 127 43 45 +3 45 119 278 +3 97 140 258 +3 125 16 14 +3 14 12 98 +3 15 12 14 +3 127 120 37 +3 110 111 65 +3 107 108 71 +3 98 125 14 +3 131 134 172 +3 94 93 103 +3 94 102 95 +3 95 1 94 +3 93 1 78 +3 1 93 94 +3 1 95 2 +3 4 96 97 +3 2 96 4 +3 78 92 93 +3 92 78 76 +3 91 76 74 +3 74 90 91 +3 88 68 66 +3 86 64 62 +3 85 62 60 +3 85 60 84 +3 82 58 56 +3 81 56 54 +3 80 54 52 +3 124 36 99 +3 10 98 12 +3 60 83 84 +3 46 124 274 +3 44 124 46 +3 42 124 44 +3 40 124 42 +3 38 124 40 +3 97 10 8 +3 66 87 88 +3 48 100 101 +3 102 94 103 +3 76 91 92 +3 72 74 75 +3 68 88 89 +3 64 86 87 +3 62 85 86 +3 58 82 83 +3 56 81 82 +3 54 80 81 +3 126 16 125 +3 17 15 14 +3 120 132 19 +3 21 19 20 +3 23 120 21 +3 16 18 19 +3 19 18 20 +3 20 18 126 +3 16 126 18 +3 90 74 72 +3 73 70 72 +3 72 70 90 +3 72 75 73 +3 119 221 263 +3 120 221 262 +3 131 222 283 +3 133 222 282 +3 121 223 291 +3 129 223 290 +3 119 224 261 +3 120 224 260 +3 227 225 265 +3 120 225 264 +3 119 226 273 +3 46 226 272 +3 225 227 277 +3 45 227 276 +3 131 228 281 +3 133 228 280 +3 131 229 285 +3 133 229 284 +3 121 230 289 +3 129 230 288 +3 121 231 293 +3 129 231 292 +3 248 232 297 +3 132 232 296 +3 252 233 301 +3 138 233 300 +3 200 234 258 +3 97 234 257 +3 119 235 260 +3 120 235 259 +3 119 236 262 +3 120 236 261 +3 119 237 264 +3 120 237 263 +3 243 238 266 +3 120 238 265 +3 138 239 268 +3 121 239 267 +3 13 240 270 +3 249 240 269 +3 119 241 272 +3 46 241 271 +3 119 242 274 +3 46 242 273 +3 238 243 276 +3 45 243 275 +3 225 244 278 +3 45 244 277 +3 131 245 280 +3 133 245 279 +3 131 246 282 +3 133 246 281 +3 131 247 284 +3 133 247 283 +3 131 248 286 +3 232 248 285 +3 240 249 288 +3 129 249 287 +3 121 250 290 +3 129 250 289 +3 121 251 292 +3 129 251 291 +3 121 252 294 +3 233 252 293 +3 229 253 296 +3 132 253 295 +3 286 254 298 +3 132 254 297 +3 231 255 300 +3 138 255 299 +3 294 256 302 +3 138 256 301 +3 257 234 200 +3 257 98 97 +3 258 140 200 +3 258 234 97 +3 259 235 119 +3 259 124 120 +3 260 224 119 +3 260 235 120 +3 261 236 119 +3 261 224 120 +3 262 221 119 +3 262 236 120 +3 263 237 119 +3 263 221 120 +3 264 225 119 +3 264 237 120 +3 265 238 227 +3 265 225 120 +3 266 127 275 +3 266 238 120 +3 267 239 138 +3 267 140 121 +3 268 139 138 +3 268 239 121 +3 269 240 13 +3 269 133 287 +3 270 121 13 +3 270 240 230 +3 271 241 119 +3 271 100 46 +3 272 226 119 +3 272 241 46 +3 273 242 119 +3 273 226 46 +3 274 124 119 +3 274 242 46 +3 275 243 266 +3 275 127 45 +3 276 227 238 +3 276 243 45 +3 277 244 225 +3 277 227 45 +3 278 119 225 +3 278 244 45 +3 279 245 131 +3 279 129 133 +3 280 228 131 +3 280 245 133 +3 281 246 131 +3 281 228 133 +3 282 222 131 +3 282 246 133 +3 283 247 131 +3 283 222 133 +3 284 229 131 +3 284 247 133 +3 285 248 131 +3 285 229 232 +3 286 130 131 +3 286 248 254 +3 287 249 269 +3 287 133 129 +3 288 230 240 +3 288 249 129 +3 289 250 121 +3 289 230 129 +3 290 223 121 +3 290 250 129 +3 291 251 121 +3 291 223 129 +3 292 231 121 +3 292 251 129 +3 293 252 121 +3 293 231 233 +3 294 139 121 +3 294 252 256 +3 295 253 229 +3 295 133 132 +3 296 232 229 +3 296 253 132 +3 297 254 248 +3 297 232 132 +3 298 130 286 +3 298 254 132 +3 299 255 231 +3 299 129 138 +3 300 233 231 +3 300 255 138 +3 301 256 252 +3 301 233 138 +3 302 139 294 +3 302 256 138 + diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remeshing_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remeshing_test.cpp new file mode 100644 index 00000000000..96331067199 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remeshing_test.cpp @@ -0,0 +1,187 @@ +// data/joint_refined.off 0.1 5 data/joint-patch.selection.txt + +#define CGAL_PMP_REMESHING_DEBUG +//#define CGAL_DUMP_REMESHING_STEPS +#define CGAL_PMP_REMESHING_VERBOSE +//#define CGAL_PMP_REMESHING_EXPENSIVE_DEBUG + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; + +typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; +typedef boost::graph_traits::edge_descriptor edge_descriptor; +typedef boost::graph_traits::vertex_descriptor vertex_descriptor; +typedef boost::graph_traits::face_descriptor face_descriptor; + +void collect_patch(const char* file, + const Mesh& m, + std::set& patch) +{ + std::ifstream in(file); + if (!in.is_open()) + return; + + std::string line; + std::size_t id; + + if (!std::getline(in, line)) { return ; } + std::istringstream vertex_line(line); + while (vertex_line >> id) { + if (id >= m.number_of_vertices()) { return ; } + //do nothing with vertices + } + + if (!std::getline(in, line)) { return ; } + std::istringstream facet_line(line); + while (facet_line >> id) { + if (id >= m.number_of_faces()) { return; } + patch.insert(Mesh::Face_index(Mesh::size_type(id))); + } + + if (!std::getline(in, line)) { return ; } + std::istringstream edge_line(line); + while (edge_line >> id) { + if (id >= m.number_of_edges()) { return; } + //do nothing with edges + } + + in.close(); +} + +void test_precondition(const char* filename, + const char* bad_selection_file) +{ + Mesh m; + std::ifstream input(filename); + if (!input || !(input >> m)){ + std::cerr << "Error: can not read file.\n"; + return; + } + std::set patch; + collect_patch(bad_selection_file, m, patch); + + std::cout << "Start remeshing of " << bad_selection_file + << " (" << patch.size() << " faces)..." << std::endl; + +#ifndef CGAL_NDEBUG //o.w. CGAL_precondition not tested + bool exception_caught = false; + try + { + PMP::isotropic_remeshing(m, patch, 0.079, + PMP::parameters::protect_constraints(true)); + } + catch (const std::exception &) + { + exception_caught = true; + } + CGAL_assertion(exception_caught); +#endif +} + +struct halfedge2edge +{ + halfedge2edge(const Mesh& m, std::vector& edges) + : m_mesh(m), m_edges(edges) + {} + void operator()(const halfedge_descriptor& h) const + { + m_edges.push_back(edge(h, m_mesh)); + } + const Mesh& m_mesh; + std::vector& m_edges; +}; + + +int main(int argc, char* argv[]) +{ +#ifdef CGAL_PMP_REMESHING_DEBUG + std::cout.precision(17); +#endif + + const char* filename = (argc > 1) ? argv[1] + : "data/joint_refined.off"; + std::ifstream input(filename); + + Mesh m; + if (!input || !(input >> m)){ + std::cerr << "Error: can not read file.\n"; + return 1; + } + + double target_edge_length = (argc > 2) ? atof(argv[2]) : 0.079; + unsigned int nb_iter = (argc > 3) ? atoi(argv[3]) : 1; + const char* selection_file = (argc > 4) ? argv[4] + : "data/joint-patch.selection.txt"; + + std::set pre_patch; + collect_patch(selection_file, m, pre_patch); + + std::cout << "Test self intersections..."; + std::vector > facets; + PMP::self_intersections(pre_patch, + m, + std::back_inserter(facets)); + if(!facets.empty()) + { + std::cout << "Input is self intersecting. STOP" << std::endl; + return 0; + } + else + std::cout << "OK." << std::endl; + + std::cout << "Split border..."; + + std::vector border; + PMP::border_halfedges(pre_patch, + boost::make_function_output_iterator(halfedge2edge(m, border)), + m); + PMP::split_long_edges(m, border, target_edge_length); + + std::cout << "done." << std::endl; + + std::set patch; + std::copy(pre_patch.begin(), pre_patch.end(), + std::inserter(patch, patch.begin())); + + std::cout << "Start remeshing of " << selection_file + << " (" << patch.size() << " faces)..." << std::endl; + + CGAL::Timer t; + t.start(); + + PMP::isotropic_remeshing(m, + patch, + target_edge_length, + PMP::parameters::number_of_iterations(nb_iter) + .protect_constraints(true) + ); + + t.stop(); + std::cout << "Remeshing took " << t.time() << std::endl; + + std::ofstream out("remeshed.off"); + out << m; + out.close(); + + //this test should make the precondition fail + test_precondition("data/joint_refined.off", + "data/joint-patch-toolargeconstraints.selection.txt"); + + return 0; +} diff --git a/Polyhedron/demo/Polyhedron/CMakeLists.txt b/Polyhedron/demo/Polyhedron/CMakeLists.txt index 74384fdbf78..fede4846fd8 100644 --- a/Polyhedron/demo/Polyhedron/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/CMakeLists.txt @@ -91,6 +91,7 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND) Polyhedron_demo_mesh_3_local_optimizers_dialog.ui Polyhedron_demo_mesh_3_smoother_dialog.ui ) qt5_wrap_ui( cameraUI_FILES Camera_positions_list.ui ) + qt5_wrap_ui( isotropicRemeshingUI_FILES Polyhedron_demo_isotropic_remeshing_dialog.ui) qt5_wrap_ui( PreferencesUI_FILES Preferences.ui ) qt5_wrap_ui( point_inside_polyhedronUI_FILES Point_inside_polyhedron_widget.ui) qt5_wrap_ui( point_set_selectionUI_FILES Point_set_selection_widget.ui) @@ -442,8 +443,11 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND) polyhedron_demo_plugin(corefinement_plugin Polyhedron_demo_corefinement_plugin) target_link_libraries(corefinement_plugin scene_polyhedron_item scene_combinatorial_map_item scene_polylines_item) - polyhedron_demo_plugin(trivial_plugin Polyhedron_demo_trivial_plugin) + polyhedron_demo_plugin(repair_polyhedron_plugin Polyhedron_demo_repair_polyhedron_plugin) + target_link_libraries(repair_polyhedron_plugin scene_polyhedron_item) + polyhedron_demo_plugin(trivial_plugin Polyhedron_demo_trivial_plugin) + # Edit polyhedron scene item and plugin if ( EIGEN3_FOUND AND "${EIGEN3_VERSION}" VERSION_GREATER "3.1.90" ) qt5_wrap_ui( editionUI_FILES Deform_mesh.ui ) @@ -513,6 +517,14 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND) polyhedron_demo_plugin(shortest_path_plugin Polyhedron_demo_shortest_path_plugin ${shortestPathUI_FILES}) target_link_libraries(shortest_path_plugin scene_polyhedron_item scene_polylines_item scene_polyhedron_selection_item scene_polyhedron_shortest_path_item scene_basic_objects) + + polyhedron_demo_plugin(detect_sharp_edges_plugin Polyhedron_demo_detect_sharp_edges_plugin) + target_link_libraries(detect_sharp_edges_plugin scene_polyhedron_item) + + + polyhedron_demo_plugin(isotropic_remeshing_plugin Polyhedron_demo_isotropic_remeshing_plugin ${isotropicRemeshingUI_FILES}) + target_link_libraries(isotropic_remeshing_plugin scene_polyhedron_item scene_polyhedron_selection_item) + qt5_wrap_ui( exampleUI_FILES Polyhedron_demo_example_plugin.ui dock_example.ui) diff --git a/Polyhedron/demo/Polyhedron/Deform_mesh.ui b/Polyhedron/demo/Polyhedron/Deform_mesh.ui index 72f24642fe4..cb959cdad52 100644 --- a/Polyhedron/demo/Polyhedron/Deform_mesh.ui +++ b/Polyhedron/demo/Polyhedron/Deform_mesh.ui @@ -6,8 +6,8 @@ 0 0 - 357 - 491 + 568 + 588 @@ -21,7 +21,7 @@ Selection - + @@ -57,7 +57,7 @@ - + @@ -104,7 +104,7 @@ - + @@ -127,6 +127,47 @@ + + + + QLayout::SetDefaultConstraint + + + + + Load ROI / Control Vertices + + + + + + + Show As Sphere + + + false + + + + + + + Show ROI + + + true + + + + + + + Save ROI / Control Vertices + + + + + @@ -178,6 +219,78 @@ + + + + Qt::DefaultContextMenu + + + Remeshing + + + false + + + + + + + + Warning : after remeshing all ROI and control vertices will be unselected. "Discard changes" will be unavailable. + + + Qt::LeftToRight + + + Remesh after deformation + + + + + + + + + If unchecked, automatic value is used (the average edge length at ROI boundary) + + + Qt::RightToLeft + + + Target edge length + + + + + + + Qt::RightToLeft + + + Nb. iterations + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + + + + + + @@ -227,45 +340,26 @@ - - - Activate Pivoting - - + + + + + Activate Pivoting + + + + + + + Activate Fixed Plane Deformation + + + + - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Update Original Positions - - - - - @@ -273,75 +367,51 @@ - - - - - - Show ROI - - - true - - - - - - - Show As Sphere - - - false - - - - - - - - - - - Save ROI / Control Vertices - - - - - - - Load ROI / Control Vertices - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - Apply and Close - - + + + + + + + Reset mesh to last version saved by "Overwrite Initial Geometry". Not available when Remeshing is ON. + + + Discard changes + + + + + + + + 0 + 0 + + + + Overwrite Initial Geometry + + + + + + + + 0 + 0 + + + + Apply and Close + + + + + + diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_detect_sharp_edges.h b/Polyhedron/demo/Polyhedron/Polyhedron_demo_detect_sharp_edges.h new file mode 100644 index 00000000000..ed07702d383 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_detect_sharp_edges.h @@ -0,0 +1,32 @@ + +#ifndef POLYHEDRON_DEMO_DETECT_SHARP_EDGES_H +#define POLYHEDRON_DEMO_DETECT_SHARP_EDGES_H + +#include + +namespace CGAL +{ + template + void reset_sharp_edges(Polyhedron* pMesh) + { + for (typename Polyhedron::Edge_iterator + eit = pMesh->edges_begin(), + end = pMesh->edges_end(); eit != end; ++eit) + { + eit->set_feature_edge(false); + } + } + + template + void detect_sharp_edges(Polyhedron* pMesh, const double angle) + { + reset_sharp_edges(pMesh); + + // Detect edges in current polyhedron + CGAL::Mesh_3::Detect_features_in_polyhedra detect_features; + detect_features.detect_sharp_edges(*pMesh, angle); + } + +}//end namespace CGAL + +#endif //POLYHEDRON_DEMO_DETECT_SHARP_EDGES_H diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_detect_sharp_edges_plugin.cpp b/Polyhedron/demo/Polyhedron/Polyhedron_demo_detect_sharp_edges_plugin.cpp new file mode 100644 index 00000000000..4fe35ddab63 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_detect_sharp_edges_plugin.cpp @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include + +#include "Scene_polyhedron_item.h" +#include "Scene_polygon_soup_item.h" +#include "Polyhedron_type.h" + +#include +#include + +#include "Polyhedron_demo_detect_sharp_edges.h" + +using namespace CGAL::Three; +class Polyhedron_demo_detect_sharp_edges_plugin : + public QObject, + public Polyhedron_demo_plugin_helper +{ + Q_OBJECT + Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) + Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") + +public: + void init(QMainWindow* mainWindow, Scene_interface* scene_interface) { + this->scene = scene_interface; + this->mw = mainWindow; + actionSharEdges = new QAction("Detect sharp features", mw); + actionSharEdges->setObjectName("detectSharpFeaturesAction"); + if(actionSharEdges) { + connect(actionSharEdges, SIGNAL(triggered()), + this, SLOT(detectSharpEdgesWithInputDialog())); + } + } + + bool applicable(QAction*) const { + Q_FOREACH(int index, scene->selectionIndices()) + { + Scene_polyhedron_item* item = + qobject_cast(scene->item(index)); + if (item) return true; + } + return false; + } + + // used by Polyhedron_demo_plugin_helper + QList actions() const { + return QList() << actionSharEdges; + } + +public Q_SLOTS: +void detectSharpEdges(bool input_dialog = false, double angle = 60); + void detectSharpEdgesWithInputDialog(); + +protected: + Kernel::Vector_3 facet_normal(Polyhedron::Facet_handle f); + bool is_sharp(Polyhedron::Halfedge_handle he); + +private: + QAction* actionSharEdges; +}; // end Polyhedron_demo_detect_sharp_edges_plugin + +void Polyhedron_demo_detect_sharp_edges_plugin::detectSharpEdgesWithInputDialog() +{ + detectSharpEdges(true); +} + +void Polyhedron_demo_detect_sharp_edges_plugin::detectSharpEdges(bool input_dialog, + double angle) +{ + typedef std::pair Poly_tuple; + + // Get selected items + QList polyhedrons; + Q_FOREACH(int index, scene->selectionIndices()) + { + Scene_polyhedron_item* item = + qobject_cast(scene->item(index)); + if(!item) + return; + + Polyhedron* pMesh = item->polyhedron(); + if(!pMesh) + return; + item->show_feature_edges(true); + polyhedrons << make_pair(index, pMesh); + } + + if(input_dialog) { + bool ok = true; + angle = QInputDialog::getDouble(NULL, + tr("Sharp edges max angle"), + tr("Angle in degrees between 0 and 180:"), + angle, // value + 0., // min + 180., // max + 2, // decimals + &ok); + if(!ok) return; + } + // Detect edges + QApplication::setOverrideCursor(Qt::WaitCursor); + + Q_FOREACH(Poly_tuple tuple, polyhedrons) + { + Polyhedron* pMesh = tuple.second; + if (!pMesh) continue; + + CGAL::detect_sharp_edges(pMesh, angle); + + // update scene + scene->itemChanged(tuple.first); + } + + // default cursor + QApplication::restoreOverrideCursor(); +} + +//Q_EXPORT_PLUGIN2(Polyhedron_demo_detect_sharp_edges_plugin, Polyhedron_demo_detect_sharp_edges_plugin) + +#include "Polyhedron_demo_detect_sharp_edges_plugin.moc" diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_edit_polyhedron_plugin.cpp b/Polyhedron/demo/Polyhedron/Polyhedron_demo_edit_polyhedron_plugin.cpp index 0364af64ce0..fcaf6af3581 100644 --- a/Polyhedron/demo/Polyhedron/Polyhedron_demo_edit_polyhedron_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_edit_polyhedron_plugin.cpp @@ -39,6 +39,7 @@ public Q_SLOTS: void on_SelectAllVerticesPushButton_clicked(); void on_DeleteCtrlVertPushButton_clicked(); void on_ApplyAndClosePushButton_clicked(); + void on_DiscardChangesPushButton_clicked(); void on_ClearROIPushButton_clicked(); void on_ShowROICheckBox_stateChanged(int state); void on_ShowAsSphereCheckBox_stateChanged(int state); @@ -113,6 +114,7 @@ void Polyhedron_demo_edit_polyhedron_plugin::init(QMainWindow* mainWindow, CGAL: connect(ui_widget.SelectAllVerticesPushButton, SIGNAL(clicked()), this, SLOT(on_SelectAllVerticesPushButton_clicked())); connect(ui_widget.DeleteCtrlVertPushButton, SIGNAL(clicked()), this, SLOT(on_DeleteCtrlVertPushButton_clicked())); connect(ui_widget.ApplyAndClosePushButton, SIGNAL(clicked()), this, SLOT(on_ApplyAndClosePushButton_clicked())); + connect(ui_widget.DiscardChangesPushButton, SIGNAL(clicked()), this, SLOT(on_DiscardChangesPushButton_clicked())); connect(ui_widget.ClearROIPushButton, SIGNAL(clicked()), this, SLOT(on_ClearROIPushButton_clicked())); connect(ui_widget.ShowROICheckBox, SIGNAL(stateChanged(int)), this, SLOT(on_ShowROICheckBox_stateChanged(int))); connect(ui_widget.ShowAsSphereCheckBox, SIGNAL(stateChanged(int)), this, SLOT(on_ShowAsSphereCheckBox_stateChanged(int))); @@ -201,6 +203,15 @@ void Polyhedron_demo_edit_polyhedron_plugin::on_ApplyAndClosePushButton_clicked( { dock_widget->setVisible(false); } +void Polyhedron_demo_edit_polyhedron_plugin::on_DiscardChangesPushButton_clicked() +{ + int item_id = scene->mainSelectionIndex(); + Scene_edit_polyhedron_item* edit_item = qobject_cast(scene->item(item_id)); + if (!edit_item) return; // the selected item is not of the right type + + edit_item->reset_deform_object(); + scene->itemChanged(edit_item); //for redraw +} void Polyhedron_demo_edit_polyhedron_plugin::on_ShowROICheckBox_stateChanged(int /*state*/) { for(CGAL::Three::Scene_interface::Item_id i = 0, end = scene->numberOfEntries(); i < end; ++i) diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_hole_filling_polyline_plugin.cpp b/Polyhedron/demo/Polyhedron/Polyhedron_demo_hole_filling_polyline_plugin.cpp index bbfd9baa297..ad55771d1e4 100644 --- a/Polyhedron/demo/Polyhedron/Polyhedron_demo_hole_filling_polyline_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_hole_filling_polyline_plugin.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,8 @@ #include #include +namespace PMP = CGAL::Polygon_mesh_processing; + template class Polyhedron_builder : public CGAL::Modifier_base { public: @@ -125,7 +128,9 @@ public Q_SLOTS: CGAL::Timer timer; timer.start(); std::vector > patch; - CGAL::Polygon_mesh_processing::triangulate_hole_polyline(*it, std::back_inserter(patch), use_DT); + CGAL::Polygon_mesh_processing::triangulate_hole_polyline(*it, + std::back_inserter(patch), + PMP::parameters::use_delaunay_triangulation(use_DT)); print_message(QString("Triangulated in %1 sec.").arg(timer.time())); if(patch.empty()) { diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_isotropic_remeshing_dialog.ui b/Polyhedron/demo/Polyhedron/Polyhedron_demo_isotropic_remeshing_dialog.ui new file mode 100644 index 00000000000..cefb9e94d95 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_isotropic_remeshing_dialog.ui @@ -0,0 +1,216 @@ + + + Isotropic_remeshing_dialog + + + true + + + + 0 + 0 + 414 + 282 + + + + Isotropic remeshing criteria + + + + + + + 15 + 75 + true + + + + NO OBJECT + + + + + + + No size + + + + + + + Remesh + + + + + + Split only border/selected edges + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Isotropic remeshing + + + + + + Number of iterations + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + nbIterations_spinbox + + + + + + + + 110 + 0 + + + + 1000.000000000000000 + + + 0.100000000000000 + + + + + + + + 110 + 0 + + + + + + + + + + + true + + + + + + + Target edge length + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + edgeLength_dspinbox + + + + + + + Protect border/selected edges + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + edgeLength_dspinbox + nbIterations_spinbox + buttonBox + + + + + buttonBox + accepted() + Isotropic_remeshing_dialog + accept() + + + 388 + 288 + + + 157 + 195 + + + + + buttonBox + rejected() + Isotropic_remeshing_dialog + reject() + + + 388 + 288 + + + 286 + 195 + + + + + diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_isotropic_remeshing_plugin.cpp b/Polyhedron/demo/Polyhedron/Polyhedron_demo_isotropic_remeshing_plugin.cpp new file mode 100644 index 00000000000..98429db04f3 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_isotropic_remeshing_plugin.cpp @@ -0,0 +1,249 @@ +//#define CGAL_PMP_REMESHING_VERBOSE +//#define CGAL_PMP_REMESHING_DEBUG +//#define CGAL_PMP_REMESHING_VERY_VERBOSE + +#include + +#include +#include + +#include "Scene_polyhedron_item.h" +#include "Scene_polyhedron_selection_item.h" +#include "Polyhedron_type.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ui_Polyhedron_demo_isotropic_remeshing_dialog.h" + +using namespace CGAL::Three; +class Polyhedron_demo_isotropic_remeshing_plugin : + public QObject, + public Polyhedron_demo_plugin_helper +{ + Q_OBJECT + Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) + Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") + +public: + void init(QMainWindow* mainWindow, Scene_interface* scene_interface) + { + this->scene = scene_interface; + this->mw = mainWindow; + actionIsotropicRemeshing_ = new QAction("Isotropic remeshing", mw); + if (actionIsotropicRemeshing_) { + connect(actionIsotropicRemeshing_, SIGNAL(triggered()), + this, SLOT(isotropic_remeshing())); + } + } + + QList actions() const { + return QList() << actionIsotropicRemeshing_; + } + + bool applicable(QAction*) const + { + return qobject_cast(scene->item(scene->mainSelectionIndex())) + || qobject_cast(scene->item(scene->mainSelectionIndex())); + } + +public Q_SLOTS: + void isotropic_remeshing() + { + const Scene_interface::Item_id index = scene->mainSelectionIndex(); + + Scene_polyhedron_item* poly_item = + qobject_cast(scene->item(index)); + + Scene_polyhedron_selection_item* selection_item = + qobject_cast(scene->item(index)); + + if (poly_item || selection_item) + { + // Create dialog box + QDialog dialog(mw); + Ui::Isotropic_remeshing_dialog ui; + ui.setupUi(&dialog); + connect(ui.buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept())); + connect(ui.buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject())); + + //connect checkbox to spinbox + connect(ui.splitEdgesOnly_checkbox, SIGNAL(toggled(bool)), + ui.nbIterations_spinbox, SLOT(setDisabled(bool))); + connect(ui.splitEdgesOnly_checkbox, SIGNAL(toggled(bool)), + ui.protect_checkbox, SLOT(setDisabled(bool))); + + //Set default parameters + bool p_ = (poly_item != NULL); + Scene_interface::Bbox bbox = p_ ? poly_item->bbox() : selection_item->bbox(); + ui.objectName->setText(p_ ? poly_item->name() : selection_item->name()); + ui.objectNameSize->setText( + tr("Object bbox size (w,h,d): %1, %2, %3") + .arg(bbox.width(), 0, 'g', 3) + .arg(bbox.height(), 0, 'g', 3) + .arg(bbox.depth(), 0, 'g', 3)); + + double diago_length = bbox.diagonal_length(); + ui.edgeLength_dspinbox->setDecimals(3); + ui.edgeLength_dspinbox->setSingleStep(0.001); + ui.edgeLength_dspinbox->setRange(1e-6 * diago_length, //min + 2. * diago_length);//max + ui.edgeLength_dspinbox->setValue(0.05 * diago_length); + + std::ostringstream oss; + oss << "Diagonal length of the Bbox of the selection to remesh is "; + oss << diago_length << "." << std::endl; + oss << "Default is 5% of it" << std::endl; + ui.edgeLength_dspinbox->setToolTip(QString::fromStdString(oss.str())); + + ui.nbIterations_spinbox->setSingleStep(1); + ui.nbIterations_spinbox->setRange(1/*min*/, 1000/*max*/); + ui.nbIterations_spinbox->setValue(1); + + ui.protect_checkbox->setChecked(false); + + // Get values + int i = dialog.exec(); + if (i == QDialog::Rejected) + { + std::cout << "Remeshing aborted" << std::endl; + return; + } + bool edges_only = ui.splitEdgesOnly_checkbox->isChecked(); + double target_length = ui.edgeLength_dspinbox->value(); + int nb_iter = ui.nbIterations_spinbox->value(); + bool protect = ui.protect_checkbox->isChecked(); + + // wait cursor + QApplication::setOverrideCursor(Qt::WaitCursor); + + QTime time; + time.start(); + + typedef boost::graph_traits::edge_descriptor edge_descriptor; + typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef boost::graph_traits::face_descriptor face_descriptor; + if (selection_item) + { + std::vector updated_selected_edges; + if (edges_only) + { + const Polyhedron& pmesh = *selection_item->polyhedron(); + std::vector edges; + BOOST_FOREACH(edge_descriptor e, selection_item->selected_edges) + { + if (selection_item->selected_facets.find(face(halfedge(e, pmesh), pmesh)) + != selection_item->selected_facets.end() + || selection_item->selected_facets.find(face(opposite(halfedge(e, pmesh), pmesh), pmesh)) + != selection_item->selected_facets.end()) + edges.push_back(e); + } + BOOST_FOREACH(face_descriptor f, selection_item->selected_facets) + { + BOOST_FOREACH(halfedge_descriptor he, halfedges_around_face(halfedge(f, pmesh), pmesh)) + { + if (selection_item->selected_facets.find(face(opposite(he, pmesh), pmesh)) + == selection_item->selected_facets.end()) + edges.push_back(edge(he, pmesh)); + } + } + CGAL::Polygon_mesh_processing::split_long_edges( + *selection_item->polyhedron() + , edges + , target_length + , std::back_inserter(updated_selected_edges) + , PMP::parameters::geom_traits(Kernel())); + } + else + { + std::vector selected( + selection_item->polyhedron()->size_of_halfedges()/2, + false); + + if (selection_item->selected_edges.empty()) + CGAL::Polygon_mesh_processing::isotropic_remeshing( + *selection_item->polyhedron() + , selection_item->selected_facets + , target_length + , CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter) + .protect_constraints(protect)); + else + CGAL::Polygon_mesh_processing::isotropic_remeshing( + *selection_item->polyhedron() + , selection_item->selected_facets + , target_length + , CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter) + .protect_constraints(protect) + .edge_is_constrained_map(selection_item->selected_edges_pmap(selected)) + ); + + } + selection_item->poly_item_changed(); + selection_item->clear_all(); + selection_item->selected_edges.insert(updated_selected_edges.begin(), + updated_selected_edges.end()); + selection_item->changed_with_poly_item(); + } + else if (poly_item) + { + if (edges_only) + { + const Polyhedron& pmesh = *poly_item->polyhedron(); + std::vector border; + CGAL::Polygon_mesh_processing::border_halfedges( + faces(*poly_item->polyhedron()), + std::back_inserter(border), + pmesh); + std::vector border_edges; + BOOST_FOREACH(halfedge_descriptor h, border) + border_edges.push_back(edge(h, pmesh)); + + CGAL::Polygon_mesh_processing::split_long_edges(*poly_item->polyhedron() + , border_edges + , target_length); + } + else + { + CGAL::Polygon_mesh_processing::isotropic_remeshing( + *poly_item->polyhedron() + , faces(*poly_item->polyhedron()) + , target_length + , CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter) + .protect_constraints(protect)); + } + poly_item->invalidate_buffers(); + Q_EMIT poly_item->itemChanged(); + } + else{ + std::cout << "Can't remesh that type of thing" << std::endl; + } + std::cout << "ok (" << time.elapsed() << " ms)" << std::endl; + + // default cursor + QApplication::restoreOverrideCursor(); + } + } + +private: + QAction* actionIsotropicRemeshing_; + +}; // end Polyhedron_demo_isotropic_remeshing_plugin + +//Q_EXPORT_PLUGIN2(Polyhedron_demo_isotropic_remeshing_plugin, +// Polyhedron_demo_isotropic_remeshing_plugin) + +#include "Polyhedron_demo_isotropic_remeshing_plugin.moc" diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_repair_polyhedron_plugin.cpp b/Polyhedron/demo/Polyhedron/Polyhedron_demo_repair_polyhedron_plugin.cpp new file mode 100644 index 00000000000..00b9729d154 --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_repair_polyhedron_plugin.cpp @@ -0,0 +1,115 @@ +#include + +#include "Scene_polyhedron_item.h" +#include +#include "Polyhedron_type.h" +#include +#include +#include "Messages_interface.h" +#include + +#include +#include +#include + +#include +#include + +using namespace CGAL::Three; +class Polyhedron_demo_repair_polyhedron_plugin : + public QObject, + public Polyhedron_demo_plugin_helper +{ + Q_OBJECT + Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) + Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") + +public: + // To silent a warning -Woverloaded-virtual + // See http://stackoverflow.com/questions/9995421/gcc-woverloaded-virtual-warnings + using Polyhedron_demo_plugin_helper::init; + + void init(QMainWindow* mainWindow, + Scene_interface* scene_interface, + Messages_interface* m) + { + this->scene = scene_interface; + this->mw = mainWindow; + this->messages = m; + + actionRemoveIsolatedVertices = new QAction(tr("Remove isolated vertices"), mw); + if (actionRemoveIsolatedVertices){ + connect(actionRemoveIsolatedVertices, SIGNAL(triggered()), + this, SLOT(on_actionRemoveIsolatedVertices_triggered())); + } + + actionRemoveDegenerateFaces = new QAction(tr("Remove degenerate faces"), mw); + if (actionRemoveDegenerateFaces){ + connect(actionRemoveDegenerateFaces, SIGNAL(triggered()), + this, SLOT(on_actionRemoveDegenerateFaces_triggered())); + } + } + + QList actions() const + { + return QList() << actionRemoveIsolatedVertices + << actionRemoveDegenerateFaces; + } + + bool applicable(QAction*) const + { + int item_id = scene->mainSelectionIndex(); + return qobject_cast( + scene->item(item_id)); + } + +public Q_SLOTS: + void on_actionRemoveIsolatedVertices_triggered(); + void on_actionRemoveDegenerateFaces_triggered(); + +private: + QAction* actionRemoveIsolatedVertices; + QAction* actionRemoveDegenerateFaces; + + Messages_interface* messages; +}; // end Polyhedron_demo_repair_polyhedron_plugin + + +void Polyhedron_demo_repair_polyhedron_plugin::on_actionRemoveIsolatedVertices_triggered() +{ + const Scene_interface::Item_id index = scene->mainSelectionIndex(); + + Scene_polyhedron_item* poly_item = + qobject_cast(scene->item(index)); + if (poly_item) + { + std::size_t nbv = + CGAL::Polygon_mesh_processing::remove_isolated_vertices( + *poly_item->polyhedron()); + messages->information(tr(" %1 isolated vertices have been removed.") + .arg(nbv)); + poly_item->invalidate_buffers(); + Q_EMIT poly_item->itemChanged(); + } +} + +void Polyhedron_demo_repair_polyhedron_plugin::on_actionRemoveDegenerateFaces_triggered() +{ + const Scene_interface::Item_id index = scene->mainSelectionIndex(); + + Scene_polyhedron_item* poly_item = + qobject_cast(scene->item(index)); + if (poly_item) + { + std::size_t nbv = + CGAL::Polygon_mesh_processing::remove_degenerate_faces( + *poly_item->polyhedron()); + messages->information(tr(" %1 degenerate faces have been removed.") + .arg(nbv)); + poly_item->invalidate_buffers(); + Q_EMIT poly_item->itemChanged(); + } +} + + +#include "Polyhedron_demo_repair_polyhedron_plugin.moc" diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_selection_plugin.cpp b/Polyhedron/demo/Polyhedron/Polyhedron_demo_selection_plugin.cpp index ac615375364..8b8b6d824cd 100644 --- a/Polyhedron/demo/Polyhedron/Polyhedron_demo_selection_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_selection_plugin.cpp @@ -97,6 +97,8 @@ public: connect(ui_widget.Keep_connected_components_button, SIGNAL(clicked()), this, SLOT(on_Keep_connected_components_button_clicked())); connect(ui_widget.Dilate_erode_button, SIGNAL(clicked()), this, SLOT(on_Dilate_erode_button_clicked())); connect(ui_widget.Create_polyhedron_item_button, SIGNAL(clicked()), this, SLOT(on_Create_polyhedron_item_button_clicked())); + connect(ui_widget.Select_sharp_edges_button, SIGNAL(clicked()), this, SLOT(on_Select_sharp_edges_button_clicked())); + QObject* scene = dynamic_cast(scene_interface); if(scene) { connect(scene, SIGNAL(itemAboutToBeDestroyed(Scene_item*)), this, SLOT(item_about_to_be_destroyed(Scene_item*))); @@ -310,6 +312,19 @@ public Q_SLOTS: print_message("Error: polyhedron item is not created!"); } } + + void on_Select_sharp_edges_button_clicked() { + Scene_polyhedron_selection_item* selection_item = get_selected_item(); + if (!selection_item) { + print_message("Error: there is no selected polyhedron selection item!"); + return; + } + + double angle = ui_widget.Sharp_angle_spinbox->value(); + selection_item->select_sharp_edges(angle); + scene->itemChanged(selection_item); + } + void on_Dilate_erode_button_clicked() { Scene_polyhedron_selection_item* selection_item = get_selected_item(); if(!selection_item) { diff --git a/Polyhedron/demo/Polyhedron/Scene_edit_polyhedron_item.cpp b/Polyhedron/demo/Polyhedron/Scene_edit_polyhedron_item.cpp index de70065c05f..09cb6c3fa7a 100644 --- a/Polyhedron/demo/Polyhedron/Scene_edit_polyhedron_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_edit_polyhedron_item.cpp @@ -1,3 +1,5 @@ +//#define CGAL_PMP_REMESHING_VERBOSE + #include "opengl_tools.h" #include "create_sphere.h" #include "Scene_edit_polyhedron_item.h" @@ -12,7 +14,6 @@ Scene_edit_polyhedron_item::Scene_edit_polyhedron_item : Scene_item(NumberOfBuffers,NumberOfVaos), ui_widget(ui_widget), poly_item(poly_item), - deform_mesh(*(poly_item->polyhedron()), Deform_mesh::Vertex_index_map(), Deform_mesh::Hedge_index_map(), Array_based_vertex_point_map(&positions)), is_rot_free(true), own_poly_item(true), k_ring_selector(poly_item, mw, Scene_polyhedron_item_k_ring_selection::Active_handle::VERTEX, true) @@ -30,6 +31,11 @@ Scene_edit_polyhedron_item::Scene_edit_polyhedron_item poly_item->set_color_vector_read_only(true); // to prevent recomputation of color vector in invalidate_buffers() poly_item->update_vertex_indices(); + deform_mesh = new Deform_mesh(*(poly_item->polyhedron()), + Deform_mesh::Vertex_index_map(), + Deform_mesh::Hedge_index_map(), + Array_based_vertex_point_map(&positions)); + length_of_axis = bbox().diagonal_length() / 15.0; // interleave events of viewer (there is only one viewer) @@ -44,36 +50,8 @@ Scene_edit_polyhedron_item::Scene_edit_polyhedron_item startTimer(0); // Required for drawing functionality - positions.resize(num_vertices(*polyhedron())*3); - normals.resize(positions.size()); - Polyhedron::Vertex_iterator vb, ve; - std::size_t counter = 0; - for(vb=polyhedron()->vertices_begin(), ve = polyhedron()->vertices_end();vb != ve; ++vb, ++counter) { - positions[counter*3] = vb->point().x(); - positions[counter*3+1] = vb->point().y(); - positions[counter*3+2] = vb->point().z(); + reset_drawing_data(); - const Polyhedron::Traits::Vector_3& n = - CGAL::Polygon_mesh_processing::compute_vertex_normal(vb, deform_mesh.halfedge_graph()); - - normals[counter*3] = n.x(); - normals[counter*3+1] = n.y(); - normals[counter*3+2] = n.z(); - } - tris.resize(polyhedron()->size_of_facets()*3); - counter = 0; - for(Polyhedron::Facet_handle fb = polyhedron()->facets_begin(); fb != polyhedron()->facets_end(); ++fb, ++counter) { - tris[counter*3] = static_cast(fb->halfedge()->vertex()->id()); - tris[counter*3+1] = static_cast(fb->halfedge()->next()->vertex()->id()); - tris[counter*3+2] = static_cast(fb->halfedge()->prev()->vertex()->id()); - } - - edges.resize(polyhedron()->size_of_halfedges()); - counter = 0; - for(Polyhedron::Edge_iterator eb = polyhedron()->edges_begin(); eb != polyhedron()->edges_end(); ++eb, ++counter) { - edges[counter*2] = static_cast(eb->vertex()->id()); - edges[counter*2+1] = static_cast(eb->opposite()->vertex()->id()); - } //Generates an integer which will be used as ID for each buffer const char vertex_shader_source_bbox[] = @@ -109,6 +87,14 @@ Scene_edit_polyhedron_item::Scene_edit_polyhedron_item bbox_program.addShaderFromSourceCode(QOpenGLShader::Fragment,fragment_shader_source); bbox_program.link(); + ui_widget->remeshing_iterations_spinbox->setValue(1); + + ui_widget->remeshing_edge_length_spinbox->setValue(length_of_axis); + ui_widget->remeshing_edge_length_spinbox->setDisabled(true); + ui_widget->remeshingEdgeLengthInput_checkBox->setChecked(false); + connect(ui_widget->remeshingEdgeLengthInput_checkBox, SIGNAL(toggled(bool)), + ui_widget->remeshing_edge_length_spinbox, SLOT(setEnabled(bool))); + //the spheres : create_Sphere(length_of_axis/15.0); invalidate_buffers(); @@ -120,8 +106,9 @@ Scene_edit_polyhedron_item::~Scene_edit_polyhedron_item() { delete_ctrl_vertices_group(false); } - if (own_poly_item) delete poly_item; + delete deform_mesh; + if (own_poly_item) delete poly_item; } ///////////////////////////// /// For the Shader gestion/// @@ -305,17 +292,81 @@ void Scene_edit_polyhedron_item::initialize_buffers(CGAL::Three::Viewer_interfac color_lines.resize(0); std::vector(color_lines).swap(color_lines); } + //vao for the frame plane + { + program = getShaderProgram(PROGRAM_WITHOUT_LIGHT, viewer); + program->bind(); + bbox_program.bind(); + vaos[Frame_plane]->bind(); + buffers[Frame_vertices].bind(); + buffers[Frame_vertices].allocate(pos_frame_plane.data(), + static_cast(pos_frame_plane.size()*sizeof(double))); + bbox_program.enableAttributeArray("vertex"); + bbox_program.setAttributeBuffer("vertex",GL_DOUBLE,0,3); + buffers[Frame_vertices].release(); + + vaos[Frame_plane]->release(); + bbox_program.release(); + program->release(); + } are_buffers_filled = true; } +void Scene_edit_polyhedron_item::reset_drawing_data() +{ + positions.clear(); + positions.resize(num_vertices(*polyhedron()) * 3); + + normals.clear(); + normals.resize(positions.size()); + + std::size_t counter = 0; + BOOST_FOREACH(vertex_descriptor vb, vertices(*polyhedron())) + { + positions[counter * 3] = vb->point().x(); + positions[counter * 3 + 1] = vb->point().y(); + positions[counter * 3 + 2] = vb->point().z(); + + const Polyhedron::Traits::Vector_3& n = + CGAL::Polygon_mesh_processing::compute_vertex_normal(vb, deform_mesh->halfedge_graph()); + normals[counter * 3] = n.x(); + normals[counter * 3 + 1] = n.y(); + normals[counter * 3 + 2] = n.z(); + + ++counter; + } + + tris.clear(); + tris.resize(polyhedron()->size_of_facets() * 3); + counter = 0; + BOOST_FOREACH(face_descriptor fb, faces(*polyhedron())) + { + tris[counter * 3] = static_cast(fb->halfedge()->vertex()->id()); + tris[counter * 3 + 1] = static_cast(fb->halfedge()->next()->vertex()->id()); + tris[counter * 3 + 2] = static_cast(fb->halfedge()->prev()->vertex()->id()); + ++counter; + } + + edges.clear(); + edges.resize(polyhedron()->size_of_halfedges()); + counter = 0; + for (Polyhedron::Edge_iterator eb = polyhedron()->edges_begin(); + eb != polyhedron()->edges_end(); ++eb, ++counter) + { + edges[counter * 2] = static_cast(eb->vertex()->id()); + edges[counter * 2 + 1] = static_cast(eb->opposite()->vertex()->id()); + } +} + void Scene_edit_polyhedron_item::compute_normals_and_vertices(void) { ROI_points.resize(0); control_points.resize(0); control_color.resize(0); - BOOST_FOREACH(vertex_descriptor vd, deform_mesh.roi_vertices()) + pos_frame_plane.resize(0); + BOOST_FOREACH(vertex_descriptor vd, deform_mesh->roi_vertices()) { - if(!deform_mesh.is_control_vertex(vd)) + if(!deform_mesh->is_control_vertex(vd)) {//gl_draw_point( vd->point() ); ROI_points.push_back(vd->point().x()); ROI_points.push_back(vd->point().y()); @@ -374,6 +425,9 @@ void Scene_edit_polyhedron_item::compute_normals_and_vertices(void) color_lines[6] = 1.0; color_lines[9] = 1.0; color_lines[13] = 1.0; color_lines[16] = 1.0; + if(ui_widget->ActivateFixedPlaneCheckBox->isChecked()) + draw_frame_plane(viewer); + } ///////////////////////////////////////////////////////// @@ -384,12 +438,90 @@ void Scene_edit_polyhedron_item::deform() for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it) { it->set_target_positions(); } - deform_mesh.deform(); + deform_mesh->deform(); poly_item->invalidate_aabb_tree(); // invalidate the AABB-tree of the poly_item Q_EMIT itemChanged(); } +void Scene_edit_polyhedron_item::remesh() +{ + const Polyhedron& g = deform_mesh->halfedge_graph(); + Array_based_vertex_point_map vpmap(&positions); + + std::set roi_facets; + std::set roi_halfedges; + BOOST_FOREACH(vertex_descriptor v, deform_mesh->roi_vertices()) + { + BOOST_FOREACH(face_descriptor fv, CGAL::faces_around_target(halfedge(v, g), g)) + { + roi_facets.insert(fv); + BOOST_FOREACH(halfedge_descriptor h, CGAL::halfedges_around_face(halfedge(fv, g), g)) + { + if (roi_halfedges.find(opposite(h, g)) == roi_halfedges.end()) //not already computed + roi_halfedges.insert(h); + } + } + } + + bool automatic_target_length = !ui_widget->remeshingEdgeLengthInput_checkBox->isChecked(); + double sum_len = 0.; + std::vector roi_border; + BOOST_FOREACH(halfedge_descriptor h, roi_halfedges) + { + if (roi_halfedges.find(opposite(h, g)) == roi_halfedges.end()) + { + roi_border.push_back(opposite(h, g)); + if (automatic_target_length) + sum_len += CGAL::sqrt(CGAL::squared_distance( + get(vpmap, source(h, g)), get(vpmap, target(h, g)))); + } + } + + if (roi_border.empty()) + automatic_target_length = false; + + double target_length = automatic_target_length + ? sum_len / (0. + roi_border.size()) + : ui_widget->remeshing_edge_length_spinbox->value(); + + unsigned int nb_iter = ui_widget->remeshing_iterations_spinbox->value(); + + std::cout << "Remeshing..."; + CGAL::Polygon_mesh_processing::isotropic_remeshing( + *polyhedron() + , roi_facets + , target_length + , CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter) + .protect_constraints(false) //no edge_is_constrained_map + .vertex_point_map(vpmap) + ); + std::cout << "done." << std::endl; + + //reset ROI from its outside border roi_border + clear_roi(); + do{ + delete_ctrl_vertices_group(false); + } + while(!ctrl_vertex_frame_map.empty()); + + poly_item->update_vertex_indices(); + poly_item->update_halfedge_indices(); + delete deform_mesh; + deform_mesh = new Deform_mesh(*(poly_item->polyhedron()), + Deform_mesh::Vertex_index_map(), + Deform_mesh::Hedge_index_map(), + vpmap); + + reset_drawing_data(); + compute_normals_and_vertices(); + + poly_item->invalidate_aabb_tree(); // invalidate the AABB tree + create_ctrl_vertices_group(); + + Q_EMIT itemChanged(); +} + void Scene_edit_polyhedron_item::timerEvent(QTimerEvent* /*event*/) { // just handle deformation - paint like selection is handled in eventFilter() if(state.ctrl_pressing && (state.left_button_pressing || state.right_button_pressing)) { @@ -435,12 +567,16 @@ bool Scene_edit_polyhedron_item::eventFilter(QObject* /*target*/, QEvent *event) // check state changes between old and current state bool ctrl_pressed_now = state.ctrl_pressing && !old_state.ctrl_pressing; bool ctrl_released_now = !state.ctrl_pressing && old_state.ctrl_pressing; - if(ctrl_pressed_now || ctrl_released_now || event->type() == QEvent::HoverMove) + if(ctrl_pressed_now || ctrl_released_now || event->type() == QEvent::HoverMove) {// activate a handle manipulated frame QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin(); const QPoint& p = viewer->mapFromGlobal(QCursor::pos()); bool need_repaint = activate_closest_manipulated_frame(p.x(), p.y()); + if (ctrl_released_now && ui_widget->RemeshingCheckBox->isChecked()){ + remesh(); + } + if(need_repaint) { Q_EMIT itemChanged(); } } @@ -460,6 +596,17 @@ void Scene_edit_polyhedron_item::draw_edges(CGAL::Three::Viewer_interface* viewe program->release(); vaos[Edges]->release(); + + vaos[Frame_plane]->bind(); + program = getShaderProgram(PROGRAM_WITHOUT_LIGHT); + attrib_buffers(viewer,PROGRAM_WITHOUT_LIGHT); + program->bind(); + program->setAttributeValue("colors", QColor(0,0,0)); + viewer->glDrawArrays(GL_LINE_LOOP, 0, (GLsizei)pos_frame_plane.size()/3); + program->release(); + vaos[Frame_plane]->release(); + + if(rendering_mode == Wireframe) { draw_ROI_and_control_vertices(viewer); } @@ -479,7 +626,39 @@ void Scene_edit_polyhedron_item::draw(CGAL::Three::Viewer_interface* viewer) con draw_edges(viewer); draw_ROI_and_control_vertices(viewer); +} +void Scene_edit_polyhedron_item::draw_frame_plane(QGLViewer* viewer) const +{ + pos_frame_plane.resize(15); + for(Ctrl_vertices_group_data_list::const_iterator hgb_data = ctrl_vertex_frame_map.begin(); hgb_data != ctrl_vertex_frame_map.end(); ++hgb_data) + { + if(hgb_data->frame == viewer->manipulatedFrame()) + { + + const double diag = scene_diag(); + qglviewer::Vec base1(1,0,0); + qglviewer::Vec base2(0,1,0); + + qglviewer::Quaternion orientation=hgb_data->frame->orientation(); + base1=orientation.rotate(base1); + base2=orientation.rotate(base2); + + qglviewer::Vec center = hgb_data->calculate_initial_center(); + + qglviewer::Vec p1 = center - diag*base1 - diag*base2; + qglviewer::Vec p2 = center + diag*base1 - diag*base2; + qglviewer::Vec p3 = center + diag*base1 + diag*base2; + qglviewer::Vec p4 = center - diag*base1 + diag*base2; + + pos_frame_plane[0] = p1.x ; pos_frame_plane[1] = p1.y; pos_frame_plane[2] =p1.z ; + pos_frame_plane[3] = p2.x ; pos_frame_plane[4] = p2.y; pos_frame_plane[5] =p2.z ; + pos_frame_plane[6] = p3.x ; pos_frame_plane[7] = p3.y; pos_frame_plane[8] =p3.z ; + pos_frame_plane[9] = p4.x ; pos_frame_plane[10]= p4.y; pos_frame_plane[11] =p4.z ; + pos_frame_plane[12] = p1.x ; pos_frame_plane[13]= p1.y; pos_frame_plane[14] =p1.z ; + are_buffers_filled = false; + } + } } void Scene_edit_polyhedron_item::draw_ROI_and_control_vertices(CGAL::Three::Viewer_interface* viewer) const { diff --git a/Polyhedron/demo/Polyhedron/Scene_edit_polyhedron_item.h b/Polyhedron/demo/Polyhedron/Scene_edit_polyhedron_item.h index b0e75dbcadd..1723b645aa9 100644 --- a/Polyhedron/demo/Polyhedron/Scene_edit_polyhedron_item.h +++ b/Polyhedron/demo/Polyhedron/Scene_edit_polyhedron_item.h @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -18,7 +18,11 @@ #include #include "ui_Deform_mesh.h" + +#include #include +#include + #include #include #include @@ -28,15 +32,15 @@ typedef Polyhedron::Vertex_handle Vertex_handle; typedef boost::graph_traits::vertex_descriptor vertex_descriptor; typedef boost::graph_traits::vertex_iterator vertex_iterator; -typedef boost::graph_traits::in_edge_iterator in_edge_iterator; -typedef boost::graph_traits::out_edge_iterator out_edge_iterator; +typedef boost::graph_traits::face_descriptor face_descriptor; +typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; struct Array_based_vertex_point_map { public: typedef vertex_descriptor key_type; typedef Polyhedron::Traits::Point_3 value_type; - typedef value_type& reference; + typedef value_type& reference; typedef boost::read_write_property_map_tag category; Array_based_vertex_point_map(std::vector* positions) : positions(positions) {} std::vector* positions; @@ -54,14 +58,25 @@ inline void put(Array_based_vertex_point_map pmap, Array_based_vertex_point_map::key_type key, - Array_based_vertex_point_map::value_type val) { + Array_based_vertex_point_map::value_type val) +{ key->point() = val; // to make things easy (ray selection after deformation, save to polyhedron after close etc), // I also change point() of vertex together with positions list // So that we do not need to pmap everywhere other than draw + if (key->id() == std::size_t(-1)) + { + key->id() = pmap.positions->size() / 3; + pmap.positions->push_back(val.x()); + pmap.positions->push_back(val.y()); + pmap.positions->push_back(val.z()); + } + else + { std::size_t pos = key->id() * 3; (*pmap.positions)[pos] = val.x(); (*pmap.positions)[pos+1] = val.y(); (*pmap.positions)[pos+2] = val.z(); + } } typedef CGAL::Surface_mesh_deformationblockSignals(true); // do not let it Q_EMIT modified, which will cause a deformation // but we are just adjusting the center so it does not require a deformation - frame->setOrientation(qglviewer::Quaternion()); + // frame->setOrientation(qglviewer::Quaternion()); frame->setPosition(frame_initial_center); frame->blockSignals(oldState); } @@ -119,6 +134,18 @@ public: deform_mesh->set_target_position(*hb, Point(rotated_and_translated.x, rotated_and_translated.y, rotated_and_translated.z) ); } } + qglviewer::Vec calculate_initial_center() const + { + qglviewer::Vec center_acc(0, 0, 0); + if (initial_positions.empty()) { return center_acc; } + + for (std::vector::const_iterator it = initial_positions.begin(); + it != initial_positions.end(); ++it) + { + center_acc += (*it); + } + return center_acc / initial_positions.size(); + } private: void reset_initial_positions() @@ -131,17 +158,6 @@ private: initial_positions.push_back(point); } } - qglviewer::Vec calculate_initial_center() - { - qglviewer::Vec center_acc(0, 0, 0); - if(initial_positions.empty()) {return center_acc; } - - for(std::vector::iterator it = initial_positions.begin(); it != initial_positions.end(); ++it) - { - center_acc += (*it); - } - return center_acc / initial_positions.size(); - } CGAL::Three::Scene_interface::Bbox calculate_initial_bbox() { if(initial_positions.empty()) {return CGAL::Three::Scene_interface::Bbox(0,0,0,0,0,0); } @@ -204,6 +220,7 @@ public: void draw_edges(CGAL::Three::Viewer_interface*) const; void draw_bbox(const CGAL::Three::Scene_interface::Bbox&) const; void draw_ROI_and_control_vertices(CGAL::Three::Viewer_interface *viewer) const; + void draw_frame_plane(QGLViewer *viewer) const; // Get wrapped polyhedron Polyhedron* polyhedron(); @@ -260,6 +277,8 @@ public Q_SLOTS: double dir_z); void deform(); // deform the mesh + void remesh(); + // members private: Ui::DeformMesh* ui_widget; @@ -279,6 +298,7 @@ private: mutable std::vector pos_axis; mutable std::vector pos_sphere; mutable std::vector normals_sphere; + mutable std::vector pos_frame_plane; mutable QOpenGLShaderProgram *program; mutable QOpenGLShaderProgram bbox_program; mutable std::size_t nb_ROI; @@ -298,7 +318,8 @@ private: Bbox_vertices, Axis_vertices, Axis_colors, - NumberOfBuffers = Axis_colors+1 + Frame_vertices, + NumberOfBuffers }; enum Vao { @@ -310,7 +331,8 @@ private: Control_points, Control_spheres, Axis, - NumberOfVaos= Axis+1 + Frame_plane, + NumberOfVaos }; mutable QOpenGLBuffer *in_bu; using Scene_item::initialize_buffers; @@ -318,11 +340,9 @@ private: void compute_normals_and_vertices(void); void compute_bbox(const CGAL::Three::Scene_interface::Bbox&); void create_Sphere(double); + void reset_drawing_data(); - - - - Deform_mesh deform_mesh; + Deform_mesh* deform_mesh; typedef std::list Ctrl_vertices_group_data_list; Ctrl_vertices_group_data_list::iterator active_group; Ctrl_vertices_group_data_list ctrl_vertex_frame_map; // keep list of group of control vertices with assoc data @@ -348,7 +368,7 @@ public: return false; } // no group of control vertices to insert - bool inserted = deform_mesh.insert_control_vertex(v); + bool inserted = deform_mesh->insert_control_vertex(v); if(inserted) { active_group->ctrl_vertices_group.push_back(v); active_group->refresh(); @@ -358,12 +378,12 @@ public: bool insert_roi_vertex(vertex_descriptor v) { - return deform_mesh.insert_roi_vertex(v); + return deform_mesh->insert_roi_vertex(v); } bool erase_control_vertex(vertex_descriptor v) { - if(deform_mesh.erase_control_vertex(v)) // API should be safe enough to do that (without checking empty group of control vertices etc.) + if(deform_mesh->erase_control_vertex(v)) // API should be safe enough to do that (without checking empty group of control vertices etc.) { refresh_all_group_centers(); // since we don't know which group of control vertices v is erased from, refresh all return true; @@ -376,7 +396,7 @@ public: bool erase_roi_vertex(vertex_descriptor v) { erase_control_vertex(v); // erase control vertex - return deform_mesh.erase_roi_vertex(v); + return deform_mesh->erase_roi_vertex(v); } void set_all_vertices_as_roi() @@ -395,7 +415,7 @@ public: delete it->frame; } ctrl_vertex_frame_map.clear(); - deform_mesh.clear_roi_vertices(); + deform_mesh->clear_roi_vertices(); create_ctrl_vertices_group(); // create one new group of control vertices } @@ -413,7 +433,7 @@ public: qglviewer::ManipulatedFrame* new_frame = new qglviewer::ManipulatedFrame(); new_frame->setRotationSensitivity(2.0f); - Control_vertices_data hgd(&deform_mesh, new_frame); + Control_vertices_data hgd(deform_mesh, new_frame); ctrl_vertex_frame_map.push_back(hgd); hgd.refresh(); @@ -439,7 +459,7 @@ public: { delete it->frame; for(std::vector::iterator v_it = it->ctrl_vertices_group.begin(); v_it != it->ctrl_vertices_group.end(); ++v_it) { - deform_mesh.erase_control_vertex(*v_it); + deform_mesh->erase_control_vertex(*v_it); } ctrl_vertex_frame_map.erase(it); break; @@ -504,6 +524,7 @@ public: { is_rot_free=true; rot_constraint.setRotationConstraintType(qglviewer::AxisPlaneConstraint::FREE); + rot_constraint.setTranslationConstraintType(qglviewer::AxisPlaneConstraint::FREE); // just block signals to prevent deformation for(Ctrl_vertices_group_data_list::iterator it = ctrl_vertex_frame_map.begin(); it != ctrl_vertex_frame_map.end(); ++it) @@ -516,8 +537,8 @@ public: { std::ofstream out(file_name); // save roi - out << deform_mesh.roi_vertices().size() << std::endl; - BOOST_FOREACH(vertex_descriptor vd, deform_mesh.roi_vertices()) + out << deform_mesh->roi_vertices().size() << std::endl; + BOOST_FOREACH(vertex_descriptor vd, deform_mesh->roi_vertices()) { out << vd->id() << " "; } @@ -543,9 +564,9 @@ public: // put vertices to vector std::vector all_vertices; - all_vertices.reserve(num_vertices(deform_mesh.halfedge_graph())); + all_vertices.reserve(num_vertices(deform_mesh->halfedge_graph())); vertex_iterator vb, ve; - for(boost::tie(vb, ve) = vertices(deform_mesh.halfedge_graph()); vb != ve; ++vb) { + for(boost::tie(vb, ve) = vertices(deform_mesh->halfedge_graph()); vb != ve; ++vb) { all_vertices.push_back(*vb); } // read roi @@ -577,16 +598,22 @@ public: void overwrite_deform_object() { - deform_mesh.overwrite_initial_geometry(); + deform_mesh->overwrite_initial_geometry(); refresh_all_group_centers(); } + void reset_deform_object() + { + deform_mesh->reset(); + refresh_all_group_centers(); + } + struct Is_selected { - Deform_mesh& dm; - Is_selected(Deform_mesh& dm) : dm(dm) {} + Deform_mesh* dm; + Is_selected(Deform_mesh* dm) : dm(dm) {} bool count(Vertex_handle vh) const { - return dm.is_roi_vertex(vh); + return dm->is_roi_vertex(vh); } }; @@ -608,7 +635,7 @@ public: boost::optional select_isolated_components(std::size_t threshold) { typedef boost::function_output_iterator Output_iterator; - Output_iterator out(&deform_mesh); + Output_iterator out(deform_mesh); Travel_isolated_components::Selection_visitor visitor(threshold, out); Travel_isolated_components().travel @@ -663,6 +690,9 @@ protected: } if(ctrl_vertex_frame_map.empty()) { return false; } + rot_constraint.setRotationConstraintType(qglviewer::AxisPlaneConstraint::FREE); + rot_constraint.setTranslationConstraintType(qglviewer::AxisPlaneConstraint::FREE); + QGLViewer* viewer = *QGLViewer::QGLViewerPool().begin(); qglviewer::Camera* camera = viewer->camera(); @@ -696,7 +726,16 @@ protected: min_it->frame->setConstraint(&rot_constraint); } else - rot_constraint.setRotationConstraintType(qglviewer::AxisPlaneConstraint::FREE); + { + if(!ui_widget->ActivatePivotingCheckBox->isChecked() && + ui_widget->ActivateFixedPlaneCheckBox->isChecked()) + { + // the constraint is local to the frame + rot_constraint.setTranslationConstraint(qglviewer::AxisPlaneConstraint::PLANE,qglviewer::Vec(0,0,1)); + rot_constraint.setRotationConstraintType(qglviewer::AxisPlaneConstraint::FORBIDDEN); + min_it->frame->setConstraint(&rot_constraint); + } + } if(viewer->manipulatedFrame() == min_it->frame) { return false; } @@ -708,17 +747,28 @@ protected: bool keyPressEvent(QKeyEvent* e); void update_normals() { - BOOST_FOREACH(vertex_descriptor vd, deform_mesh.roi_vertices()) + BOOST_FOREACH(vertex_descriptor vd, deform_mesh->roi_vertices()) { std::size_t id = vd->id(); const Polyhedron::Traits::Vector_3& n = - CGAL::Polygon_mesh_processing::compute_vertex_normal(vd, deform_mesh.halfedge_graph()); + CGAL::Polygon_mesh_processing::compute_vertex_normal(vd, deform_mesh->halfedge_graph()); normals[id*3] = n.x(); normals[id*3+1] = n.y(); normals[id*3+2] = n.z(); } } + + double scene_diag() const { + const double& xdelta = bbox().xmax - bbox().xmin; + const double& ydelta = bbox().ymax - bbox().ymin; + const double& zdelta = bbox().zmax - bbox().zmin; + const double diag = std::sqrt(xdelta*xdelta + + ydelta*ydelta + + zdelta*zdelta); + return diag * 0.5; + } + }; // end class Scene_edit_polyhedron_item #endif // SCENE_EDIT_POLYHEDRON_ITEM_H diff --git a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp index 610d19d3c7d..5311019cf36 100644 --- a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp @@ -480,7 +480,7 @@ Scene_polyhedron_item::compute_normals_and_vertices(void) const he != poly->edges_end(); he++) { - if(he->is_feature_edge()) continue; + if (!show_feature_edges_m && he->is_feature_edge()) continue; const Point& a = he->vertex()->point(); const Point& b = he->opposite()->vertex()->point(); positions_lines.push_back(a.x()); @@ -512,11 +512,8 @@ Scene_polyhedron_item::compute_normals_and_vertices(void) const positions_lines.push_back(b.y()); positions_lines.push_back(b.z()); positions_lines.push_back(1.0); - - } - //set the colors compute_colors(); } @@ -593,6 +590,7 @@ Scene_polyhedron_item::Scene_polyhedron_item() : Scene_item(NbOfVbos,NbOfVaos), poly(new Polyhedron), show_only_feature_edges_m(false), + show_feature_edges_m(false), facet_picking_m(false), erase_next_picked_facet_m(false), plugin_has_set_color_vector_m(false) @@ -610,6 +608,7 @@ Scene_polyhedron_item::Scene_polyhedron_item(Polyhedron* const p) : Scene_item(NbOfVbos,NbOfVaos), poly(p), show_only_feature_edges_m(false), + show_feature_edges_m(false), facet_picking_m(false), erase_next_picked_facet_m(false), plugin_has_set_color_vector_m(false) @@ -627,6 +626,7 @@ Scene_polyhedron_item::Scene_polyhedron_item(const Polyhedron& p) : Scene_item(NbOfVbos,NbOfVaos), poly(new Polyhedron(p)), show_only_feature_edges_m(false), + show_feature_edges_m(false), facet_picking_m(false), erase_next_picked_facet_m(false), plugin_has_set_color_vector_m(false) @@ -776,12 +776,20 @@ QMenu* Scene_polyhedron_item::contextMenu() connect(actionShowOnlyFeatureEdges, SIGNAL(toggled(bool)), this, SLOT(show_only_feature_edges(bool))); - QAction* actionPickFacets = - menu->addAction(tr("Facets picking")); - actionPickFacets->setCheckable(true); - actionPickFacets->setObjectName("actionPickFacets"); - connect(actionPickFacets, SIGNAL(toggled(bool)), - this, SLOT(enable_facets_picking(bool))); + QAction* actionShowFeatureEdges = + menu->addAction(tr("Show feature edges")); + actionShowFeatureEdges->setCheckable(true); + actionShowFeatureEdges->setChecked(show_feature_edges_m); + actionShowFeatureEdges->setObjectName("actionShowFeatureEdges"); + connect(actionShowFeatureEdges, SIGNAL(toggled(bool)), + this, SLOT(show_feature_edges(bool))); + + QAction* actionPickFacets = + menu->addAction(tr("Facets picking")); + actionPickFacets->setCheckable(true); + actionPickFacets->setObjectName("actionPickFacets"); + connect(actionPickFacets, SIGNAL(toggled(bool)), + this, SLOT(enable_facets_picking(bool))); QAction* actionEraseNextFacet = menu->addAction(tr("Erase next picked facet")); @@ -812,6 +820,13 @@ void Scene_polyhedron_item::show_only_feature_edges(bool b) Q_EMIT itemChanged(); } +void Scene_polyhedron_item::show_feature_edges(bool b) +{ + show_feature_edges_m = b; + invalidate_buffers(); + Q_EMIT itemChanged(); +} + void Scene_polyhedron_item::enable_facets_picking(bool b) { facet_picking_m = b; @@ -857,14 +872,16 @@ void Scene_polyhedron_item::draw(CGAL::Three::Viewer_interface* viewer) const { } // Points/Wireframe/Flat/Gouraud OpenGL drawing in a display list -void Scene_polyhedron_item::draw_edges(CGAL::Three::Viewer_interface* viewer) const { - if(!are_buffers_filled) +void Scene_polyhedron_item::draw_edges(CGAL::Three::Viewer_interface* viewer) const +{ + if (!are_buffers_filled) { compute_normals_and_vertices(); initialize_buffers(viewer); } vaos[Edges]->bind(); + attrib_buffers(viewer, PROGRAM_WITHOUT_LIGHT); program = getShaderProgram(PROGRAM_WITHOUT_LIGHT); program->bind(); diff --git a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.h b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.h index c6b832a0f4f..901a01cc075 100644 --- a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.h +++ b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.h @@ -66,6 +66,7 @@ public Q_SLOTS: virtual void invalidate_buffers(); virtual void selection_changed(bool); virtual void setColor(QColor c); + virtual void show_feature_edges(bool); void show_only_feature_edges(bool); void enable_facets_picking(bool); void set_erase_next_picked_facet(bool); @@ -102,9 +103,10 @@ private: typedef std::vector Color_vector; typedef Polyhedron::Facet_iterator Facet_iterator; - Color_vector colors_; + Color_vector colors_; bool show_only_feature_edges_m; + bool show_feature_edges_m; bool facet_picking_m; bool erase_next_picked_facet_m; //the following variable is used to indicate if the color vector must not be automatically updated. diff --git a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item_k_ring_selection.h b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item_k_ring_selection.h index 4b8ef86fd47..ee299a0ffbd 100644 --- a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item_k_ring_selection.h +++ b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item_k_ring_selection.h @@ -20,7 +20,8 @@ class SCENE_POLYHEDRON_ITEM_K_RING_SELECTION_EXPORT Scene_polyhedron_item_k_ring { Q_OBJECT public: - struct Active_handle { enum Type{ VERTEX = 0, FACET = 1, EDGE = 2 }; }; + struct Active_handle { + enum Type{ VERTEX = 0, FACET = 1, EDGE = 2 , CONNECTED_COMPONENT = 3}; }; typedef boost::graph_traits::edge_descriptor edge_descriptor; @@ -75,8 +76,9 @@ public Q_SLOTS: void facet_has_been_selected(void* void_ptr) { is_active=true; - if(active_handle_type != Active_handle::FACET) { return; } - process_selection( static_cast(void_ptr)->halfedge()->facet() ); + if (active_handle_type == Active_handle::FACET + || active_handle_type == Active_handle::CONNECTED_COMPONENT) + process_selection(static_cast(void_ptr)->halfedge()->facet()); } void edge_has_been_selected(void* void_ptr) { diff --git a/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.h b/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.h index 19a432a9fa1..cf5547c1b0b 100644 --- a/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.h +++ b/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.h @@ -12,6 +12,7 @@ #include #include #include +#include "Polyhedron_demo_detect_sharp_edges.h" #include #include @@ -19,6 +20,7 @@ #include #include +#include namespace PMP = CGAL::Polygon_mesh_processing; @@ -252,6 +254,10 @@ public: typedef boost::unordered_set Selection_set_facet; typedef boost::unordered_set Selection_set_edge; + Polyhedron* polyhedron() + { + return this->poly_item->polyhedron(); + } using Scene_polyhedron_item_decorator::draw; virtual void draw(CGAL::Three::Viewer_interface*) const; @@ -394,6 +400,7 @@ public: case Active_handle::VERTEX: select_all(); break; case Active_handle::FACET: + case Active_handle::CONNECTED_COMPONENT: select_all(); break; case Active_handle::EDGE: selected_edges.insert(edges(*polyhedron()).first, edges(*polyhedron()).second); @@ -421,6 +428,7 @@ public: case Active_handle::VERTEX: clear(); break; case Active_handle::FACET: + case Active_handle::CONNECTED_COMPONENT: clear(); break; case Active_handle::EDGE: clear(); break; @@ -436,6 +444,12 @@ public: Q_EMIT itemChanged(); } + void clear_all(){ + clear(); + clear(); + clear(); + } + boost::optional get_minimum_isolated_component() { switch(get_active_handle_type()) { case Active_handle::VERTEX: @@ -490,9 +504,10 @@ public: dilate_selection(steps); break; case Active_handle::FACET: + case Active_handle::CONNECTED_COMPONENT: dilate_selection(steps); break; - default: + case Active_handle::EDGE: dilate_selection(steps); } } @@ -503,9 +518,10 @@ public: erode_selection(-steps); break; case Active_handle::FACET: + case Active_handle::CONNECTED_COMPONENT: erode_selection(-steps); break; - default: + case Active_handle::EDGE: erode_selection(-steps); } } @@ -551,8 +567,8 @@ public: }; template - void dilate_selection(unsigned int steps) { - + void dilate_selection(unsigned int steps) + { typedef Selection_traits Tr; Tr tr(this); @@ -681,6 +697,19 @@ public: return out->size_of_vertices() > 0; } + void select_sharp_edges(const double angle) + { + CGAL::detect_sharp_edges(polyhedron(), angle); + + BOOST_FOREACH(edge_descriptor e, edges(*polyhedron())) + { + Polyhedron::Halfedge_handle h = halfedge(e, *polyhedron()); + if (h->is_feature_edge()) + selected_edges.insert(e); + } + invalidate_buffers(); + } + void changed_with_poly_item() { // no need to update indices poly_item->invalidate_buffers(); @@ -746,10 +775,10 @@ protected: } } - template - void has_been_selected(const std::set& selection) + template + bool treat_selection(const HandleRange& selection) { - if(!visible()) { return; } + typedef typename HandleRange::value_type HandleType; Selection_traits tr(this); bool any_change = false; @@ -762,6 +791,48 @@ protected: any_change |= (tr.container().erase(h)!=0); } if(any_change) { invalidate_buffers(); Q_EMIT itemChanged(); } + return any_change; + } + + Facet_handle face(Facet_handle fh) + { return fh; } + Facet_handle face(Vertex_handle) + { return boost::graph_traits::null_face(); } + Facet_handle face(edge_descriptor) + { return boost::graph_traits::null_face(); } + + template + void has_been_selected(const std::set& selection) + { + if(!visible()) { return; } + + bool any_change = false; + + if (get_active_handle_type() == Active_handle::CONNECTED_COMPONENT) + { + Selection_traits tr(this); + tr.update_indices(); + + std::vector mark(tr.size(), false); + BOOST_FOREACH(edge_descriptor e, selected_edges) + mark[tr.id(e)] = true; + + std::vector selected_cc; + CGAL::Polygon_mesh_processing::connected_component( + face(*selection.begin()), + *polyhedron(), + std::back_inserter(selected_cc), + CGAL::Polygon_mesh_processing::parameters::edge_is_constrained_map( + Is_selected_property_map(mark))); + + any_change = treat_selection(selected_cc); + } + else + { + any_change = treat_selection(selection); + } + if(any_change) { Q_EMIT changed_with_poly_item(); } } public: diff --git a/Polyhedron/demo/Polyhedron/Selection_widget.ui b/Polyhedron/demo/Polyhedron/Selection_widget.ui index e67698ec800..499a78cea2a 100644 --- a/Polyhedron/demo/Polyhedron/Selection_widget.ui +++ b/Polyhedron/demo/Polyhedron/Selection_widget.ui @@ -6,15 +6,15 @@ 0 0 - 399 - 556 + 455 + 684 Selection - + @@ -46,6 +46,11 @@ Edge + + + Connected component (facet) + + @@ -53,165 +58,244 @@ - - - + + + Qt::Vertical - - - - - - - - - - - Insertion - - - true - - - - - - - Removal - - - - - - - - - - - - - Brush &size: - - - Brush_size_spin_box - - - - - - - - - - - - - - - - - - Select &All - - - - - - - &Clear - - - - - - - - - + + + 20 + 40 + + + - - - - - - - - - - - - - Isolated &Component Size: - - - Threshold_size_spin_box - - - - - - - 999999999 - - - 8 - - - - - - - &Get Minimum - - - - - - - - - Select &Isolated Components Below Threshold - - - - - - - + + + + + + + + + Insertion + + + true + + + + + + + Removal + + + + + + + + + + + + + Brush &size: + + + Brush_size_spin_box + + + + + + + + + + + + + + + + + + Select &All + + + + + + + &Clear + + + + + + - - - + + + Qt::Vertical - - - - - Dilate or erode selection: - - - - - - - -50 - - - 50 - - - - - - - Apply - - - - - + + + 20 + 40 + + + + + + + + + + + + Isolated &Component Size: + + + Threshold_size_spin_box + + + + + + + 999999999 + + + 8 + + + + + + + &Get Minimum + + + + + + + + + Select &Isolated Components Below Threshold + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Sharp edges angle: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + 180 + + + 60 + + + + + + + Select + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Dilate or erode selection: + + + + + + + -50 + + + 50 + + + + + + + Apply + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + @@ -234,6 +318,13 @@ + + + + Erase Selected Facets from Polyhedron Item + + + @@ -244,13 +335,6 @@ - - - - Erase Selected Facets from Polyhedron Item - - - diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index f76330bdc7b..ec85adfd8f6 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -2474,7 +2474,7 @@ private: //------------------------------------------------------- private data reindex.resize(sm.num_vertices()); int n = 0; BOOST_FOREACH(Vertex_index v, sm.vertices()){ - os << sm.point(v) << std::endl; + os << sm.point(v) << '\n'; reindex[v]=n++; } @@ -2483,7 +2483,7 @@ private: //------------------------------------------------------- private data BOOST_FOREACH(Vertex_index v, CGAL::vertices_around_face(sm.halfedge(f),sm)){ os << " " << reindex[v]; } - os << "\n"; + os << '\n'; } return os; } diff --git a/Surface_mesh_skeletonization/doc/Surface_mesh_skeletonization/PackageDescription.txt b/Surface_mesh_skeletonization/doc/Surface_mesh_skeletonization/PackageDescription.txt index 8191ab03f5f..836c766d446 100644 --- a/Surface_mesh_skeletonization/doc/Surface_mesh_skeletonization/PackageDescription.txt +++ b/Surface_mesh_skeletonization/doc/Surface_mesh_skeletonization/PackageDescription.txt @@ -37,6 +37,9 @@ - `CGAL::Mean_curvature_flow_skeletonization` + \todo doc+code: mention that to get a better skeleton that is closer to the medial axis, + the surface must be sufficiently well sampled so that the Voronoi poles lie on the media axis (see Amenta's paper). + Propose the usage of the isotropic remeshing and see if we add a boolean to do it automatically in the api (correspondance would be broken) \todo code: implement the random sampling of surface using the work started by Alexandru during its gsoc to get a better approximation of poles \todo code: expose in polygon mesh processing the function to compute the voronoi pole of a close triangle mesh \todo code: expose in polygon mesh processing the function to remesh locally a triangle mesh with the angle and edge length parameters