// Copyright (c) 2009 INRIA Sophia-Antipolis (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$ // SPDX-License-Identifier: GPL-3.0+ // // // Author(s) : Stephane Tayeb // //****************************************************************************** // File Description : //****************************************************************************** #ifndef CGAL_MESH_3_TRIANGULATION_HELPERS_H #define CGAL_MESH_3_TRIANGULATION_HELPERS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace CGAL { namespace Mesh_3 { template class Triangulation_helpers { typedef typename Tr::Geom_traits Gt; typedef typename Gt::FT FT; typedef typename Gt::Vector_3 Vector_3; // If `Tr` is not a triangulation that has defined Bare_point, // use Point_3 as defined in the traits class. typedef typename boost::mpl::eval_if_c< CGAL::internal::Has_nested_type_Bare_point::value, typename CGAL::internal::Bare_point_type, boost::mpl::identity >::type Bare_point; // 'Point' is either a bare point or a weighted point, depending on the triangulation. // Since 'Triangulation_helpers' can be templated by an unweighted triangulation, // this is one of the rare cases where we use 'Point'. typedef typename Tr::Point Point; typedef typename Tr::Vertex Vertex; typedef typename Tr::Vertex_handle Vertex_handle; typedef typename Tr::Cell Cell; typedef typename Tr::Cell_handle Cell_handle; typedef typename Tr::Cell_iterator Cell_iterator; typedef std::vector Cell_vector; /** * A functor to reset visited flag of each facet of a cell */ struct Reset_facet_visited { void operator()(const Cell_handle& c) const { for ( int i=0; i<4 ; ++i ) c->reset_visited(i); } }; /** * A functor to get the point of a vertex vh, but replacing * it by m_p when vh == m_vh */ struct Point_getter { /// When the requested will be about vh, the returned point will be p /// instead of vh->point() Point_getter(const Vertex_handle &vh, const Point&p) : m_vh(vh), m_p(p) {} const Point& operator()(const Vertex_handle &vh) const { return (vh == m_vh ? m_p : vh->point()); } private: const Vertex_handle m_vh; const Point &m_p; }; public: /// Constructor / Destructor Triangulation_helpers() {} ~Triangulation_helpers() {} /** * Returns true if moving \c v to \c p makes no topological * change in \c tr */ bool no_topological_change(Tr& tr, const Vertex_handle v, const Vector_3& move, const Point& p, Cell_vector& cells_tos) const; bool no_topological_change__without_set_point( const Tr& tr, const Vertex_handle v, const Point& p, Cell_vector& cells_tos) const; bool no_topological_change(Tr& tr, const Vertex_handle v, const Vector_3& move, const Point& p) const; bool no_topological_change__without_set_point( const Tr& tr, const Vertex_handle v, const Point& p) const; bool inside_protecting_balls(const Tr& tr, const Vertex_handle v, const Bare_point& p) const; /** * Returns the squared distance from \c vh to its closest vertex * * \pre `vh` is not the infinite vertex */ template // Two versions to distinguish using 'Has_visited_for_vertex_extractor' FT get_sq_distance_to_closest_vertex(const Tr& tr, const Vertex_handle& vh, const Cell_vector& incident_cells, typename boost::enable_if_c::type* = NULL) const; // @todo are the two versions really worth it, I can't tell the difference from a time POV... template FT get_sq_distance_to_closest_vertex(const Tr& tr, const Vertex_handle& vh, const Cell_vector& incident_cells, typename boost::disable_if_c::type* = NULL) const; private: /** * Returns true if \c v is well_oriented on each cell of \c cell_tos */ // For sequential version bool well_oriented(const Tr& tr, const Cell_vector& cell_tos) const; // For parallel version bool well_oriented(const Tr& tr, const Cell_vector& cell_tos, const Point_getter& pg) const; }; template bool Triangulation_helpers:: no_topological_change(Tr& tr, const Vertex_handle v0, const Vector_3& move, const Point& p, Cell_vector& cells_tos) const { if(tr.point(v0) == p) return true; // For periodic triangulations, calling this function actually takes longer than // just removing and re-inserting the point directly because the periodic mesh // triangulation's side_of_power_sphere() function is somewhat brute-forcy: // it has to check for 27 copies of the query point to determine correctly the side. // Thus, we simply return 'false' if the triangulation is a periodic triangulation. // // Note that the function was nevertheless adapted to work with periodic triangulation // so this hack can be disabled if one day 'side_of_power_sphere()' is improved. if(boost::is_same::value) return false; typename Gt::Construct_opposite_vector_3 cov = tr.geom_traits().construct_opposite_vector_3_object(); bool np = true; const Point fp = tr.point(v0); // move the point tr.set_point(v0, move, p); if(!well_oriented(tr, cells_tos)) { // Reset (restore) v0 tr.set_point(v0, cov(move), fp); return false; } // Reset visited tags of facets std::for_each(cells_tos.begin(), cells_tos.end(), Reset_facet_visited()); for ( typename Cell_vector::iterator cit = cells_tos.begin() ; cit != cells_tos.end() ; ++cit ) { Cell_handle c = *cit; for(int j=0; j<4; j++) { // Treat each facet only once if(c->is_facet_visited(j)) continue; // Set facet and its mirrored version as visited Cell_handle cj = c->neighbor(j); int mj = tr.mirror_index(c, j); c->set_facet_visited(j); cj->set_facet_visited(mj); if(tr.is_infinite(c->vertex(j))) { if(tr.side_of_power_sphere(c, tr.point(cj->vertex(mj)), false) != CGAL::ON_UNBOUNDED_SIDE) { np = false; break; } } else { if(tr.side_of_power_sphere(cj, tr.point(c->vertex(j)), false) != CGAL::ON_UNBOUNDED_SIDE) { np = false; break; } } } } // Reset (restore) v0 tr.set_point(v0, cov(move), fp); return np; } template bool Triangulation_helpers:: no_topological_change__without_set_point( const Tr& tr, const Vertex_handle v0, const Point& p, Cell_vector& cells_tos) const { bool np = true; Point_getter pg(v0, p); if(!well_oriented(tr, cells_tos, pg)) { return false; } // Reset visited tags of facets std::for_each(cells_tos.begin(), cells_tos.end(), Reset_facet_visited()); for ( typename Cell_vector::iterator cit = cells_tos.begin() ; cit != cells_tos.end() ; ++cit ) { Cell_handle c = *cit; for(int j=0; j<4; j++) { // Treat each facet only once if(c->is_facet_visited(j)) continue; // Set facet and it's mirror's one visited Cell_handle cj = c->neighbor(j); int mj = tr.mirror_index(c, j); c->set_facet_visited(j); cj->set_facet_visited(mj); Vertex_handle v1 = c->vertex(j); if(tr.is_infinite(v1)) { // Build a copy of c, and replace V0 by a temporary vertex (position "p") typename Cell_handle::value_type c_copy (*c); int i_v0; typename Vertex_handle::value_type v; if (c_copy.has_vertex(v0, i_v0)) { v.set_point(p); c_copy.set_vertex(i_v0, Tr::Triangulation_data_structure::Vertex_range::s_iterator_to(v)); } Cell_handle c_copy_h = Tr::Triangulation_data_structure::Cell_range::s_iterator_to(c_copy); if(tr.side_of_power_sphere(c_copy_h, pg(cj->vertex(mj)), false) != CGAL::ON_UNBOUNDED_SIDE) { np = false; break; } } else { // Build a copy of cj, and replace V0 by a temporary vertex (position "p") typename Cell_handle::value_type cj_copy (*cj); int i_v0; typename Vertex_handle::value_type v; if (cj_copy.has_vertex(v0, i_v0)) { v.set_point(p); cj_copy.set_vertex(i_v0, Tr::Triangulation_data_structure::Vertex_range::s_iterator_to(v)); } Cell_handle cj_copy_h = Tr::Triangulation_data_structure::Cell_range::s_iterator_to(cj_copy); if(tr.side_of_power_sphere(cj_copy_h, pg(v1), false) != CGAL::ON_UNBOUNDED_SIDE) { np = false; break; } } } } return np; } template bool Triangulation_helpers:: no_topological_change(Tr& tr, const Vertex_handle v0, const Vector_3& move, const Point& p) const { Cell_vector cells_tos; cells_tos.reserve(64); tr.incident_cells(v0, std::back_inserter(cells_tos)); return no_topological_change(tr, v0, move, p, cells_tos); } template bool Triangulation_helpers:: no_topological_change__without_set_point( const Tr& tr, const Vertex_handle v0, const Point& p) const { Cell_vector cells_tos; cells_tos.reserve(64); tr.incident_cells(v0, std::back_inserter(cells_tos)); return no_topological_change__without_set_point(tr, v0, p, cells_tos); } template bool Triangulation_helpers:: inside_protecting_balls(const Tr& tr, const Vertex_handle v, const Bare_point& p) const { typename Gt::Compare_weighted_squared_radius_3 cwsr = tr.geom_traits().compare_weighted_squared_radius_3_object(); Vertex_handle nv = tr.nearest_power_vertex(p, v->cell()); const Point& nvwp = tr.point(nv); if(cwsr(nvwp, FT(0)) == CGAL::SMALLER) { typename Tr::Geom_traits::Construct_point_3 cp = tr.geom_traits().construct_point_3_object(); const Point& nvwp = tr.point(nv); // 'true' if the distance between 'p' and 'nv' is smaller or equal than the weight of 'nv' return (cwsr(nvwp , - tr.min_squared_distance(p, cp(nvwp))) != CGAL::LARGER); } return false; } /// Return the squared distance from vh to its closest vertex /// if `Has_visited_for_vertex_extractor` is `true` template template typename Triangulation_helpers::FT Triangulation_helpers:: get_sq_distance_to_closest_vertex(const Tr& tr, const Vertex_handle& vh, const Cell_vector& incident_cells, typename boost::enable_if_c::type*) const { CGAL_precondition(!tr.is_infinite(vh)); typedef std::vector Vertex_container; // There is no need to use tr.min_squared_distance() here because we are computing // distances between 'v' and a neighbor within their common cell, which means // that even if we are using a periodic triangulation, the distance is correctly computed. typename Gt::Compute_squared_distance_3 csqd = tr.geom_traits().compute_squared_distance_3_object(); typename Gt::Construct_point_3 cp = tr.geom_traits().construct_point_3_object(); Vertex_container treated_vertices; FT min_sq_dist = std::numeric_limits::infinity(); for(typename Cell_vector::const_iterator cit = incident_cells.begin(); cit != incident_cells.end(); ++cit) { const Cell_handle c = (*cit); const int k = (*cit)->index(vh); const Point& wpvh = tr.point(c, k); // For each vertex of the cell for(int i=1; i<4; ++i) { const int n = (k+i)&3; const Vertex_handle& vn = c->vertex(n); if(vn == Vertex_handle() || tr.is_infinite(vn) || vn->visited_for_vertex_extractor) continue; vn->visited_for_vertex_extractor = true; treated_vertices.push_back(vn); const Point& wpvn = tr.point(c, n); const FT sq_d = csqd(cp(wpvh), cp(wpvn)); if(sq_d < min_sq_dist) min_sq_dist = sq_d; } } for(std::size_t i=0; i < treated_vertices.size(); ++i) treated_vertices[i]->visited_for_vertex_extractor = false; return min_sq_dist; } /// Return the squared distance from vh to its closest vertex /// if `Has_visited_for_vertex_extractor` is `false` template template typename Triangulation_helpers::FT Triangulation_helpers:: get_sq_distance_to_closest_vertex(const Tr& tr, const Vertex_handle& vh, const Cell_vector& incident_cells, typename boost::disable_if_c::type*) const { CGAL_precondition(!tr.is_infinite(vh)); typedef CGAL::Hash_handles_with_or_without_timestamps Hash_fct; typedef boost::unordered_set Vertex_container; typedef typename Vertex_container::iterator VC_it; // There is no need to use tr.min_squared_distance() here because we are computing // distances between 'v' and a neighbor within their common cell, which means // that even if we are using a periodic triangulation, the distance is correctly computed. typename Gt::Compute_squared_distance_3 csqd = tr.geom_traits().compute_squared_distance_3_object(); typename Gt::Construct_point_3 cp = tr.geom_traits().construct_point_3_object(); Vertex_container treated_vertices; FT min_sq_dist = std::numeric_limits::infinity(); for(typename Cell_vector::const_iterator cit = incident_cells.begin(); cit != incident_cells.end(); ++cit) { const Cell_handle c = (*cit); const int k = (*cit)->index(vh); const Point& wpvh = tr.point(c, k); // For each vertex of the cell for(int i=1; i<4; ++i) { const int n = (k+i)&3; const Vertex_handle& vn = c->vertex(n); if(vn == Vertex_handle() || tr.is_infinite(vn)) continue; std::pair is_insert_succesful = treated_vertices.insert(vn); if(! is_insert_succesful.second) // vertex has already been treated continue; const Point& wpvn = tr.point(c, n); const FT sq_d = csqd(cp(wpvh), cp(wpvn)); if(sq_d < min_sq_dist) min_sq_dist = sq_d; } } return min_sq_dist; } /// This function well_oriented is called by no_topological_change after the /// position of the vertex has been (tentatively) modified. template bool Triangulation_helpers:: well_oriented(const Tr& tr, const Cell_vector& cells_tos) const { typedef typename Tr::Geom_traits Gt; typename Gt::Orientation_3 orientation = tr.geom_traits().orientation_3_object(); typename Gt::Construct_point_3 cp = tr.geom_traits().construct_point_3_object(); typename Cell_vector::const_iterator it = cells_tos.begin(); for( ; it != cells_tos.end() ; ++it) { Cell_handle c = *it; if( tr.is_infinite(c) ) { int iv = c->index(tr.infinite_vertex()); Cell_handle cj = c->neighbor(iv); int mj = tr.mirror_index(c, iv); const Point& mjwp = tr.point(cj, mj); const Point& fwp1 = tr.point(c, (iv+1)&3); const Point& fwp2 = tr.point(c, (iv+2)&3); const Point& fwp3 = tr.point(c, (iv+3)&3); if(orientation(cp(mjwp), cp(fwp1), cp(fwp2), cp(fwp3)) != CGAL::NEGATIVE) return false; } else { const Point& cwp0 = tr.point(c, 0); const Point& cwp1 = tr.point(c, 1); const Point& cwp2 = tr.point(c, 2); const Point& cwp3 = tr.point(c, 3); if(orientation(cp(cwp0), cp(cwp1), cp(cwp2), cp(cwp3)) != CGAL::POSITIVE) return false; } } return true; } /// Another version for the parallel version /// Here, the set_point is not done before, but we use a Point_getter instance /// to get the point of a vertex. template bool Triangulation_helpers:: well_oriented(const Tr& tr, const Cell_vector& cells_tos, const Point_getter& pg) const { typedef typename Tr::Geom_traits Gt; typename Gt::Orientation_3 orientation = tr.geom_traits().orientation_3_object(); typename Gt::Construct_point_3 cp = tr.geom_traits().construct_point_3_object(); typename Cell_vector::const_iterator it = cells_tos.begin(); for( ; it != cells_tos.end() ; ++it) { Cell_handle c = *it; if( tr.is_infinite(c) ) { int iv = c->index(tr.infinite_vertex()); Cell_handle cj = c->neighbor(iv); int mj = tr.mirror_index(c, iv); if(orientation(cp(pg(cj->vertex(mj))), cp(pg(c->vertex((iv+1)&3))), cp(pg(c->vertex((iv+2)&3))), cp(pg(c->vertex((iv+3)&3)))) != CGAL::NEGATIVE) return false; } else if(orientation(cp(pg(c->vertex(0))), cp(pg(c->vertex(1))), cp(pg(c->vertex(2))), cp(pg(c->vertex(3)))) != CGAL::POSITIVE) return false; } return true; } } // end namespace Mesh_3 } //namespace CGAL #endif // CGAL_MESH_3_TRIANGULATION_HELPERS_H