// Copyright (c) 2014, 2017, 2018 GeometryFactory (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). // // $URL: https://github.com/CGAL/cgal/blob/v5.1/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h $ // $Id: locate.h a84927d 2020-07-23T17:15:44+02:00 Laurent Rineau // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Mael Rouxel-Labbé, // Stephen Kiazyk // #ifndef CGAL_POLYGON_MESH_PROCESSING_LOCATE_H #define CGAL_POLYGON_MESH_PROCESSING_LOCATE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Everywhere in this file: // If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) // such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance // between the coordinates in `bc` and the vertices of the face `f` is the following: // - `w0` corresponds to `source(halfedge(f, tm), tm)` // - `w1` corresponds to `target(halfedge(f, tm), tm)` // - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` namespace CGAL { namespace Polygon_mesh_processing { namespace internal { // The Ray must have the same ambient dimension as the property map's value type (aka, the point type) template ::value> struct Ray_type_selector { typedef typename CGAL::Kernel_traits::type Kernel; typedef typename Kernel::Ray_2 type; }; template struct Ray_type_selector { typedef typename CGAL::Kernel_traits::type Kernel; typedef typename Kernel::Ray_3 type; }; // Just for convenience template > struct Location_traits { typedef typename GetVertexPointMap::const_type VertexPointMap; typedef typename boost::property_traits::value_type Point; typedef typename internal::Ray_type_selector::type Ray; typedef typename GetGeomTraits::type Geom_traits; typedef typename Geom_traits::FT FT; typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef std::array Barycentric_coordinates; typedef std::pair Face_location; }; } // end namespace internal /// \ingroup PMP_locate_grp /// /// A variant used in the function `get_descriptor_from_location()`. template using descriptor_variant = boost::variant::vertex_descriptor, typename boost::graph_traits::halfedge_descriptor, typename boost::graph_traits::face_descriptor>; /// \ingroup PMP_locate_grp /// /// A triplet of coordinates describing the barycentric coordinates of a point /// with respect to the vertices of a triangular face. /// /// \sa `Face_location` template using Barycentric_coordinates = std::array; /// \ingroup PMP_locate_grp /// /// If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` template using Face_location = std::pair::face_descriptor, Barycentric_coordinates >; // forward declarations template bool is_in_face(const std::pair::face_descriptor, std::array >& loc, const TriangleMesh& tm); template descriptor_variant get_descriptor_from_location(const std::pair::face_descriptor, std::array >& loc, const TriangleMesh& tm); template Face_location locate_in_face(typename boost::graph_traits::halfedge_descriptor hd, const FT t, const TriangleMesh& tm); // end of forward declarations namespace internal { template OutputIterator incident_faces(const std::pair::face_descriptor, std::array >& location, const TriangleMesh& tm, OutputIterator out) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::face_descriptor face_descriptor; const descriptor_variant dv = get_descriptor_from_location(location, tm); if(const vertex_descriptor* vd_ptr = boost::get(&dv)) { const vertex_descriptor vd = *vd_ptr; for(face_descriptor fd : faces_around_target(halfedge(vd, tm), tm)) *out++ = fd; } else if(const halfedge_descriptor* hd_ptr = boost::get(&dv)) { const halfedge_descriptor hd = *hd_ptr; *out++ = face(hd, tm); *out++ = face(opposite(hd, tm), tm); } else { const face_descriptor fd = boost::get(dv); *out++ = fd; } return out; } // Snapping coordinates for robustness template bool snap_coordinates_to_border(std::array& coords, const FT tolerance = std::numeric_limits::epsilon()) { #ifdef CGAL_PMP_LOCATE_DEBUG std::cout << "Pre-snapping: " << coords[0] << " " << coords[1] << " " << coords[2] << std::endl; std::cout << "Sum: " << coords[0] + coords[1] + coords[2] << std::endl; std::cout << "tolerance: " << tolerance << std::endl; #endif // To still keep a sum roughly equals to 1, keep in memory the small changes FT residue(0); bool snapped = false; for(int i=0; i<3; ++i) { if(CGAL::abs(coords[i]) <= tolerance) { snapped = true; residue += coords[i]; coords[i] = FT(0); } else if(CGAL::abs(FT(1) - coords[i]) <= tolerance) { snapped = true; residue -= FT(1) - coords[i]; coords[i] = FT(1); } } // Dump the residue into one of the barycentric values that is neither 0 nor 1 for(int i=0; i<3; ++i) { if(coords[i] != FT(0) && coords[i] != FT(1)) { coords[i] += residue; break; } } #ifdef CGAL_PMP_LOCATE_DEBUG std::cout << "Post-snapping: " << coords[0] << " " << coords[1] << " " << coords[2] << std::endl; std::cout << "Sum: " << coords[0] + coords[1] + coords[2] << std::endl; #endif return snapped; } template bool snap_location_to_border(std::pair::face_descriptor, std::array >& loc, const TriangleMesh /*tm*/, const FT tolerance = std::numeric_limits::epsilon()) { return snap_coordinates_to_border(loc.second, tolerance); } template struct Barycentric_coordinate_calculator // 2D version { std::array operator()(const P& ip, const P& iq, const P& ir, const P& iquery, const K& k) const { typedef typename K::FT FT; typedef typename K::Vector_2 Vector_2; typename K::Construct_point_2 cp2 = k.construct_point_2_object(); typename K::Construct_vector_2 cv2 = k.construct_vector_2_object(); typename K::Compute_scalar_product_2 csp2 = k.compute_scalar_product_2_object(); const typename K::Point_2& p = cp2(ip); const typename K::Point_2& q = cp2(iq); const typename K::Point_2& r = cp2(ir); const typename K::Point_2& query = cp2(iquery); Vector_2 v0 = cv2(p, q); Vector_2 v1 = cv2(p, r); Vector_2 v2 = cv2(p, query); FT d00 = csp2(v0, v0); FT d01 = csp2(v0, v1); FT d11 = csp2(v1, v1); FT d20 = csp2(v2, v0); FT d21 = csp2(v2, v1); FT denom = d00 * d11 - d01 * d01; CGAL_assertion((d00 * d11 - d01 * d01) != FT(0)); // denom != 0 FT v = (d11 * d20 - d01 * d21) / denom; FT w = (d00 * d21 - d01 * d20) / denom; return CGAL::make_array(FT(FT(1) - v - w), v, w); } }; template struct Barycentric_coordinate_calculator { std::array operator()(const P& ip, const P& iq, const P& ir, const P& iquery, const K& k) const { typedef typename K::FT FT; typedef typename K::Vector_3 Vector_3; typename K::Construct_point_3 cp3 = k.construct_point_3_object(); typename K::Construct_vector_3 cv3 = k.construct_vector_3_object(); typename K::Compute_scalar_product_3 csp3 = k.compute_scalar_product_3_object(); const typename K::Point_3& p = cp3(ip); const typename K::Point_3& q = cp3(iq); const typename K::Point_3& r = cp3(ir); const typename K::Point_3& query = cp3(iquery); Vector_3 v0 = cv3(p, q); Vector_3 v1 = cv3(p, r); Vector_3 v2 = cv3(p, query); FT d00 = csp3(v0, v0); FT d01 = csp3(v0, v1); FT d11 = csp3(v1, v1); FT d20 = csp3(v2, v0); FT d21 = csp3(v2, v1); CGAL_assertion((d00 * d11 - d01 * d01) != FT(0)); // denom != 0 FT denom_inv = FT(1) / (d00 * d11 - d01 * d01); FT v = (d11 * d20 - d01 * d21) * denom_inv; FT w = (d00 * d21 - d01 * d20) * denom_inv; return CGAL::make_array(FT(FT(1) - v - w), v, w); } }; template struct Barycentric_point_constructor // 2D version { typedef typename K::FT FT; P operator()(const P& p, const FT wp, const P& q, const FT wq, const P& r, const FT wr, const K& /*k*/) const { FT sum = wp + wq + wr; CGAL_assertion(sum != FT(0)); // In theory, this should be compute_x_2(compute_point_2(...)) and construct_P() at the end... FT x = (wp * p.x() + wq * q.x() + wr * r.x()) / sum; FT y = (wp * p.y() + wq * q.y() + wr * r.y()) / sum; return P(x, y); } }; template struct Barycentric_point_constructor // 3D version { typedef typename K::FT FT; P operator()(const P& p, const FT wp, const P& q, const FT wq, const P& r, const FT wr, const K& /*k*/) const { FT sum = wp + wq + wr; CGAL_assertion(sum != FT(0)); FT x = (wp * p.x() + wq * q.x() + wr * r.x()) / sum; FT y = (wp * p.y() + wq * q.y() + wr * r.y()) / sum; FT z = (wp * p.z() + wq * q.z() + wr * r.z()) / sum; return P(x, y, z); } }; } // namespace internal /// \ingroup PMP_locate_grp /// /// \brief Given a set of three points and a query point, computes the barycentric /// coordinates of the query point with respect to the first three points. /// /// \tparam GeomTraits the type of a geometric traits. Must be a model of `Kernel` and be compatible /// with the template parameter `Point`. /// \tparam Point the type of a geometric 2D or 3D point /// /// \param p,q,r three points with respect to whom the barycentric coordinates of `query` will be computed /// \param query the query point whose barycentric coordinates will be computed /// \param gt an instance of the geometric traits /// /// \pre `p`, `q`, and `r` are not collinear. /// \pre `query` lies on the plane defined by `p`, `q`, and `r`. /// template std::array barycentric_coordinates(const Point& p, const Point& q, const Point& r, const Point& query, const GeomTraits& gt) { internal::Barycentric_coordinate_calculator calculator; return calculator(p, q, r, query, gt); } template std::array::type::FT, 3> barycentric_coordinates(const Point& p, const Point& q, const Point& r, const Point& query) { typedef typename CGAL::Kernel_traits::type Kernel; return barycentric_coordinates(p, q, r, query, Kernel()); } /// \name Random Location Generation /// @{ /// \ingroup PMP_locate_grp /// /// \brief returns a random point over the halfedge `hd`, as a location. /// /// \details The random point is chosen on the halfedge, meaning that all /// its barycentric coordinates are positive. It is constructed by uniformly generating /// a value `t` between `0` and `1` and setting the barycentric coordinates to `t`, `1-t`, /// and `0` for respetively the source and target of `hd`, and the third vertex. /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param hd a halfedge of `tm` /// \param tm a triangulated surface mesh /// \param rnd optional random number generator /// template Face_location random_location_on_halfedge(typename boost::graph_traits::halfedge_descriptor hd, const TriangleMesh& tm, CGAL::Random& rnd = get_default_random()) { CGAL_precondition(CGAL::is_triangle_mesh(tm)); const int h_id = halfedge_index_in_face(hd, tm); const FT t(rnd.uniform_real(0., 1.)); std::array coordinates; coordinates[h_id] = t; coordinates[(h_id+1)%3] = FT(1)-t; coordinates[(h_id+2)%3] = FT(0); return std::make_pair(face(hd, tm), coordinates); } /// \ingroup PMP_locate_grp /// /// \brief returns a random point over the face `fd`, as a location. /// /// \details The random point is on the face, meaning that all its barycentric coordinates /// are positive. It is constructed by uniformly picking a value `u` between `0` and `1`, /// a value `v` between `1-u`, and setting the barycentric coordinates to `u`, `v`, and /// `1-u-v` for respectively the source and target of `halfedge(fd, tm)`, and the third point. /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param fd a face of `tm` /// \param tm a triangulated surface mesh /// \param rnd optional random number generator /// template Face_location random_location_on_face(typename boost::graph_traits::face_descriptor fd, const TriangleMesh& tm, CGAL::Random& rnd = get_default_random()) { CGAL_USE(tm); CGAL_precondition(CGAL::is_triangle_mesh(tm)); CGAL_precondition(fd != boost::graph_traits::null_face()); // calling 'rnd.uniform real' with double in case FT comes from an EPECK kernel (which doesn't seem to work too well) FT u(rnd.uniform_real(0., 1.)); FT v(rnd.uniform_real(0., CGAL::to_double(FT(1) - u))); return std::make_pair(fd, CGAL::make_array(u, v, FT(FT(1) - u - v))); } /// \ingroup PMP_locate_grp /// /// \brief returns a random point over the mesh `tm`. /// /// \details The returned location is obtained by choosing a random face of the mesh and /// a random point on that face. The barycentric coordinates of the point in the face /// are thus all positive. Note that all faces have the same probability to be chosen. /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param tm a triangulated surface mesh /// \param rnd optional random number generator /// /// \sa `random_location_on_face()` /// template Face_location random_location_on_mesh(const TriangleMesh& tm, CGAL::Random& rnd = get_default_random()) { typedef typename boost::graph_traits::face_descriptor face_descriptor; CGAL_precondition(CGAL::is_triangle_mesh(tm)); face_descriptor fd = CGAL::internal::random_face_in_mesh(tm, rnd); return random_location_on_face(fd, tm, rnd); } /// @} /// \ingroup PMP_locate_grp /// /// \brief Given a location, returns a descriptor to the simplex of smallest dimension /// on which the point corresponding to the location lies. /// /// \details In other words: /// - if the point lies on a vertex, this function returns a `boost::graph_traits::%vertex_descriptor` `v`; /// - if the point lies on a halfedge, this function returns a `boost::graph_traits::%halfedge_descriptor` `hd` /// (note that in that case, `loc.first == face(hd, tm)` holds). /// - otherwise, this function returns a `boost::graph_traits::%face_descriptor` /// `fd` (equal to `loc.first`). /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param loc a location with `loc.first` a face of `tm` /// \param tm a triangulated surface mesh /// /// \pre `loc.first` is a face descriptor corresponding to a face of `tm`. /// \pre `loc` describes the barycentric coordinates of a point that lives within the face (boundary included), /// meaning the barycentric coordinates are all positive. /// template descriptor_variant #ifdef DOXYGEN_RUNNING // just for convenience because template alias do not allow template deduction get_descriptor_from_location(const Face_location& loc, #else get_descriptor_from_location(const std::pair::face_descriptor, std::array >& loc, #endif const TriangleMesh& tm) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef Barycentric_coordinates Barycentric_coordinates; const face_descriptor fd = loc.first; const Barycentric_coordinates& bar = loc.second; CGAL_precondition(CGAL::is_triangle_mesh(tm)); CGAL_precondition(fd != boost::graph_traits::null_face()); CGAL_precondition(is_in_face(loc, tm)); // the first barycentric coordinate corresponds to source(halfedge(fd, tm), tm) halfedge_descriptor hd = prev(halfedge(fd, tm), tm); // check if the point is a vertex for(int i=0; i<3; ++i) { if(bar[i] == FT(1)) // coordinate at target(hd, tm) return target(hd, tm); hd = next(hd, tm); } CGAL_assertion(hd == prev(halfedge(fd, tm), tm)); // check if the point is on an edge for(int i=0; i<3; ++i) { if(bar[i] == FT(0)) // coordinate at target(hd, tm) return prev(hd, tm); hd = next(hd, tm); } return fd; } /// \ingroup PMP_locate_grp /// /// \brief Given a location in a face, returns the geometric position described /// by these coordinates, as a point. /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" /// /// \param loc the location from which a point is constructed /// \param tm a triangulated surface mesh /// \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below /// /// \cgalNamedParamsBegin /// \cgalParamNBegin{vertex_point_map} /// \cgalParamDescription{a property map associating points to the vertices of `tm`} /// \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and `%Point_3` as value type} /// \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} /// \cgalParamNEnd /// /// \cgalParamNBegin{geom_traits} /// \cgalParamDescription{an instance of a geometric traits class} /// \cgalParamType{a class model of `Kernel`} /// \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} /// \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} /// \cgalParamExtra{If such traits class is provided, its type `FT` must be identical /// to the template parameter `FT` of this function.} /// \cgalParamNEnd /// \cgalNamedParamsEnd /// /// \pre `loc.first` is a face descriptor corresponding to a face of `tm`. /// /// \returns a point whose type is the same as the value type of the vertex point property map /// provided by the user or via named parameters, or the internal point map of the mesh `tm`. /// template #ifdef DOXYGEN_RUNNING Point construct_point(const Face_location& loc, #else typename internal::Location_traits::Point construct_point(const std::pair::face_descriptor, std::array >& loc, #endif const TriangleMesh& tm, const NamedParameters& np) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename GetGeomTraits::type Geom_traits; typedef typename GetVertexPointMap::const_type VertexPointMap; typedef typename boost::property_traits::value_type Point; typedef typename boost::property_traits::reference Point_reference; using parameters::get_parameter; using parameters::choose_parameter; CGAL_precondition(CGAL::is_triangle_mesh(tm)); VertexPointMap vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), get_const_property_map(boost::vertex_point, tm)); Geom_traits gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); halfedge_descriptor hd = halfedge(loc.first, tm); const Point_reference p0 = get(vpm, source(hd, tm)); const Point_reference p1 = get(vpm, target(hd, tm)); const Point_reference p2 = get(vpm, target(next(hd, tm), tm)); internal::Barycentric_point_constructor bp_constructor; return bp_constructor(p0, loc.second[0], p1, loc.second[1], p2, loc.second[2], gt); } template typename property_map_value::type #ifdef DOXYGEN_RUNNING construct_point(const Face_location& loc, #else construct_point(const std::pair::face_descriptor, std::array >& loc, #endif const TriangleMesh& tm) { return construct_point(loc, tm, parameters::all_default()); } /// \name Location Predicates /// @{ /// \ingroup PMP_locate_grp /// /// \brief Given a location, returns whether the location is on the vertex `vd` or not. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param loc a location with `loc.first` a face of `tm` /// \param vd a vertex of `tm` /// \param tm a triangulated surface mesh /// /// \pre `loc.first` is a face descriptor corresponding to a face of `tm`. /// template bool #ifdef DOXYGEN_RUNNING is_on_vertex(const Face_location& loc, #else is_on_vertex(const std::pair::face_descriptor, std::array >& loc, #endif const typename boost::graph_traits::vertex_descriptor vd, const TriangleMesh& tm) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; if(!is_in_face(loc, tm)) return false; const descriptor_variant dv = get_descriptor_from_location(loc, tm); if(const vertex_descriptor* vd_ptr = boost::get(&dv)) return (vd == *vd_ptr); return false; } /// \ingroup PMP_locate_grp /// /// \brief Given a location, returns whether this location is on the halfedge `hd` or not. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param loc a location with `loc.first` a face of `tm` /// \param hd a halfedge of `tm` /// \param tm a triangulated surface mesh /// /// \pre `loc.first` is a face descriptor corresponding to a face of `tm`. /// template bool #ifdef DOXYGEN_RUNNING is_on_halfedge(const Face_location& loc, #else is_on_halfedge(const std::pair::face_descriptor, std::array >& loc, #endif const typename boost::graph_traits::halfedge_descriptor hd, const TriangleMesh& tm) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; if(!is_in_face(loc, tm)) return false; const descriptor_variant dv = get_descriptor_from_location(loc, tm); if(const vertex_descriptor* vd_ptr = boost::get(&dv)) return (*vd_ptr == source(hd, tm) || *vd_ptr == target(hd, tm)); else if(const halfedge_descriptor* hd_ptr = boost::get(&dv)) return (*hd_ptr == hd); return false; } /// \ingroup PMP_locate_grp /// /// \brief Given a set of barycentric coordinates, returns whether those barycentric /// coordinates correspond to a point within the face (boundary included), /// that is, if all the barycentric coordinates are positive. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param bar an array of barycentric coordinates /// \param tm a triangulated surface mesh /// template bool #ifdef DOXYGEN_RUNNING is_in_face(const Barycentric_coordinates& bar, #else is_in_face(const std::array& bar, #endif const TriangleMesh& tm) { CGAL_USE(tm); CGAL_precondition(CGAL::is_triangle_mesh(tm)); for(int i=0; i<3; ++i) { // "|| bar[i] > 1." is not needed because if everything is positive and the sum is '1', // then each coefficient is below '1'. if(bar[i] < FT(0)) return false; } return true; } /// \ingroup PMP_locate_grp /// /// \brief Given a location, returns whether the location is in the face (boundary included) or not. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param loc a location with `loc.first` a face of `tm` /// \param tm a triangulated surface mesh /// /// \pre `loc.first` is a face descriptor corresponding to a face of `tm`. /// template bool #ifdef DOXYGEN_RUNNING is_in_face(const Face_location& loc, #else is_in_face(const std::pair::face_descriptor, std::array >& loc, #endif const TriangleMesh& tm) { return is_in_face(loc.second, tm); } /// \ingroup PMP_locate_grp /// /// \brief Given a location, returns whether the location is on the boundary of the face or not. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param loc a location with `loc.first` a face of `tm` /// \param tm a triangulated surface mesh /// /// \pre `loc.first` is a face descriptor corresponding to a face of `tm`. /// template bool #ifdef DOXYGEN_RUNNING is_on_face_border(const Face_location& loc, #else is_on_face_border(const std::pair::face_descriptor, std::array >& loc, #endif const TriangleMesh& tm) { if(!is_in_face(loc, tm)) return false; const Barycentric_coordinates& bar = loc.second; for(int i=0; i<3; ++i) if(bar[i] == FT(0)) return true; return false; } /// \ingroup PMP_locate_grp /// /// \brief Given a location, returns whether the location is on the border of the mesh or not. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param loc a location with `loc.first` a face of `tm` /// \param tm a triangulated surface mesh /// /// \pre `loc.first` is a face descriptor corresponding to a face of `tm`. /// template bool #ifdef DOXYGEN_RUNNING is_on_mesh_border(const Face_location& loc, #else is_on_mesh_border(const std::pair::face_descriptor, std::array >& loc, #endif const TriangleMesh& tm) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::face_descriptor face_descriptor; const face_descriptor fd = loc.first; const Barycentric_coordinates& bar = loc.second; if(!is_in_face(bar, tm)) return false; // the first barycentric coordinate corresponds to source(halfedge(fd, tm), tm) halfedge_descriptor hd = prev(halfedge(fd, tm), tm); // check if the point is a vertex for(int i=0; i<3; ++i) { if(bar[i] == FT(1)) // coordinate at target(hd, tm) return bool(CGAL::is_border(target(hd, tm), tm)); hd = next(hd, tm); } CGAL_assertion(hd == prev(halfedge(fd, tm), tm)); // check if the point is on an edge for(int i=0; i<3; ++i) { if(bar[i] == FT(0)) // coordinate at target(hd, tm) return CGAL::is_border(edge(prev(hd, tm), tm), tm); hd = next(hd, tm); } // point is strictly within the face, so it's not on the border return false; } /// @} /// \name Point Location /// @{ /// \ingroup PMP_locate_grp /// /// \brief returns the location of the given vertex `vd` as a location, /// that is an ordered pair specifying a face incident to `vd` /// and the barycentric coordinates of the vertex `vd` in that face. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param vd a vertex of `tm` /// \param tm a triangulated surface mesh /// /// \pre `vd` is not an isolated vertex /// template Face_location locate_vertex(typename boost::graph_traits::vertex_descriptor vd, const TriangleMesh& tm) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef Face_location Face_location; halfedge_descriptor hd = halfedge(vd, tm); // Find a real face in case 'hd' is a border halfedge for(halfedge_descriptor thd : halfedges_around_target(hd, tm)) { if(!is_border(thd, tm)) { hd = thd; break; } } CGAL_postcondition(!CGAL::is_border(hd, tm)); // must find a 'real' face incident to 'vd' face_descriptor fd = face(hd, tm); CGAL_assertion(target(hd, tm) == vd); CGAL_assertion(fd != boost::graph_traits::null_face()); // isolated vertex if(fd == boost::graph_traits::null_face()) return Face_location(); FT coords[3] = { FT(0), FT(0), FT(0) }; hd = next(hd, tm); // so that source(hd, tm) == vd and it's simpler to handle 'index_in_face' int halfedge_local_index = halfedge_index_in_face(hd, tm); coords[halfedge_local_index] = FT(1); return std::make_pair(fd, CGAL::make_array(coords[0], coords[1], coords[2])); } /// \ingroup PMP_locate_grp /// /// \brief returns the location of a given vertex as a location in `fd`, /// that is an ordered pair composed of `fd` and of the barycentric coordinates /// of the vertex in `fd`. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param vd a vertex of `tm` and a vertex of the face `fd` /// \param fd a face of `tm` /// \param tm a triangulated surface mesh /// /// \pre `fd` is not the null face /// template Face_location locate_vertex(const typename boost::graph_traits::vertex_descriptor vd, const typename boost::graph_traits::face_descriptor fd, const TriangleMesh& tm) { CGAL_precondition(fd != boost::graph_traits::null_face()); FT coords[3] = { FT(0), FT(0), FT(0) }; std::size_t vertex_local_index = vertex_index_in_face(vd, fd, tm); coords[vertex_local_index] = FT(1); return std::make_pair(fd, CGAL::make_array(coords[0], coords[1], coords[2])); } /// \ingroup PMP_locate_grp /// /// \brief Given a point described by a halfedge `hd` and a scalar `t` /// as `p = (1 - t) * source(hd, tm) + t * target(hd, tm)`, /// returns this location along the given edge as a location, that is /// an ordered pair specifying a face containing the location and the /// barycentric coordinates of that location in that face. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param hd a halfedge of `tm` /// \param t the parametric distance of the desired point along `hd` /// \param tm a triangulated surface mesh /// template Face_location locate_on_halfedge(const typename boost::graph_traits::halfedge_descriptor hd, const FT t, const TriangleMesh& tm) { typedef typename boost::graph_traits::face_descriptor face_descriptor; face_descriptor fd = face(hd, tm); int edge_local_index = halfedge_index_in_face(hd, tm); const FT one_minus_t(FT(1) - t); FT coords[3] = { FT(0), FT(0), FT(0) }; coords[edge_local_index] = one_minus_t; coords[(edge_local_index + 1) % 3] = t; return std::make_pair(fd, CGAL::make_array(coords[0], coords[1], coords[2])); } /// \ingroup PMP_locate_grp /// /// \brief Given a point `query` and a face `fd` of a triangulated surface mesh, /// returns this location as a location, that is /// an ordered pair composed of `fd` and of the barycentric coordinates of /// `query` with respect to the vertices of `fd`. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam TriangleMesh must be a model of `FaceGraph` /// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" /// /// \param query a point, whose type is equal to the value type of the vertex point property map /// (either user-provided via named parameters or the internal point map of the mesh `tm`) /// \param fd a face of `tm` /// \param tm a triangulated surface mesh /// \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below /// /// \cgalNamedParamsBegin /// \cgalParamNBegin{vertex_point_map} /// \cgalParamDescription{a property map associating points to the vertices of `tm`} /// \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and `%Point_3` as value type} /// \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} /// \cgalParamNEnd /// /// \cgalParamNBegin{geom_traits} /// \cgalParamDescription{an instance of a geometric traits class} /// \cgalParamType{a class model of `Kernel`} /// \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} /// \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} /// \cgalParamExtra{If such traits class is provided, its type `FT` must be identical /// to the template parameter `FT` of this function.} /// \cgalParamNEnd /// /// \cgalParamNBegin{snapping_tolerance} /// \cgalParamDescription{a tolerance value used to snap barycentric coordinates} /// \cgalParamType{double} /// \cgalParamDefault{`0`} /// \cgalParamExtra{Depending on the geometric traits used, the computation of the barycentric coordinates /// might be an inexact construction, thus leading to sometimes surprising values /// (e.g. a triplet `[0.5, 0.5, -1-e17]` for a point at the middle of an edge). /// The coordinates will be snapped towards `0` and `1` if the difference is smaller /// than the tolerance value, while still ensuring that the total sum of the coordinates is `1`.} /// \cgalParamNEnd /// \cgalNamedParamsEnd /// /// \pre `fd` is not the null face /// /// \returns a face location. The type `FT` is deduced from the geometric traits, either provided by /// the user via named parameters (with `geom_traits`) or using `CGAL::Kernel_traits` /// and the point type of the vertex point property map in use. /// template #ifdef DOXYGEN_RUNNING Face_location locate_in_face(const Point& query, #else typename internal::Location_traits::Face_location locate_in_face(const typename internal::Location_traits::Point& query, #endif const typename boost::graph_traits::face_descriptor fd, const TriangleMesh& tm, const NamedParameters& np) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename GetVertexPointMap::const_type VertexPointMap; typedef typename boost::property_traits::value_type Point; typedef typename boost::property_traits::reference Point_reference; typedef typename GetGeomTraits::type Geom_traits; typedef typename Geom_traits::FT FT; using parameters::get_parameter; using parameters::choose_parameter; VertexPointMap vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), get_const_property_map(boost::vertex_point, tm)); Geom_traits gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); FT snap_tolerance = choose_parameter(get_parameter(np, internal_np::snapping_tolerance), 0); CGAL_precondition(fd != boost::graph_traits::null_face()); vertex_descriptor vd0 = source(halfedge(fd, tm), tm); vertex_descriptor vd1 = target(halfedge(fd, tm), tm); vertex_descriptor vd2 = target(next(halfedge(fd, tm), tm), tm); const Point_reference p0 = get(vpm, vd0); const Point_reference p1 = get(vpm, vd1); const Point_reference p2 = get(vpm, vd2); std::array coords = barycentric_coordinates(p0, p1, p2, query, gt); if(snap_tolerance != FT(0) && !is_in_face(coords, tm)) { std::cerr << "Warning: point " << query << " is not in the input face" << std::endl; std::cerr << "Coordinates: " << coords[0] << " " << coords[1] << " " << coords[2] << std::endl; // Try to to snap the coordinates, hoping the problem is just a -1e-17ish epsilon // pushing the coordinates over the edge internal::snap_coordinates_to_border(coords, snap_tolerance); } return std::make_pair(fd, coords); } #ifndef DOXYGEN_RUNNING // because this is in the middle of a @{ @} doxygen group template typename internal::Location_traits::Face_location locate_in_face(const typename internal::Location_traits::Point& query, const typename boost::graph_traits::face_descriptor f, const TriangleMesh& tm) { return locate_in_face(query, f, tm, parameters::all_default()); } #endif /// \ingroup PMP_locate_grp /// /// \brief Given a location and a second face adjacent to the first, returns the location of the point in the second face. /// /// \details If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`) /// such that `bc` is the triplet of barycentric coordinates `(w0, w1, w2)`, the correspondance /// between the coordinates in `bc` and the vertices of the face `f` is the following: /// - `w0` corresponds to `source(halfedge(f, tm), tm)` /// - `w1` corresponds to `target(halfedge(f, tm), tm)` /// - `w2` corresponds to `target(next(halfedge(f, tm), tm), tm)` /// /// \tparam FT must be a model of `FieldNumberType` /// \tparam TriangleMesh must be a model of `FaceGraph` /// /// \param loc the first location, with `loc.first` being a face of `tm` /// \param fd the second face, adjacent to `loc.first` /// \param tm the triangle mesh to which `fd` belongs /// /// \pre `loc` corresponds to a point that lies on a face incident to both `loc.first` and `fd`. /// template Face_location #ifdef DOXYGEN_RUNNING locate_in_adjacent_face(const Face_location& loc, #else locate_in_adjacent_face(const std::pair::face_descriptor, std::array >& loc, #endif const typename boost::graph_traits::face_descriptor fd, const TriangleMesh& tm) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; CGAL_assertion_code(typedef typename boost::graph_traits::face_descriptor face_descriptor;) if(loc.first == fd) return loc; Face_location loc_in_fd = std::make_pair(fd, CGAL::make_array(FT(0), FT(0), FT(0))); const descriptor_variant dv = get_descriptor_from_location(loc, tm); if(const vertex_descriptor* vd_ptr = boost::get(&dv)) { int index_of_vd = vertex_index_in_face(*vd_ptr, fd, tm); loc_in_fd.second[index_of_vd] = FT(1); // Note that the barycentric coordinates were initialized to 0, // so the second and third coordinates are already set up properly. } else if(const halfedge_descriptor* hd_ptr = boost::get(&dv)) { // Note that, here, we know that we are _not_ on a vertex const halfedge_descriptor hd = *hd_ptr; const halfedge_descriptor opp_hd = opposite(hd, tm); CGAL_assertion(face(hd, tm) == loc.first); CGAL_assertion(face(opp_hd, tm) == fd); CGAL_assertion(loc.first != boost::graph_traits::null_face()); CGAL_assertion(fd != boost::graph_traits::null_face()); const int index_of_hd = halfedge_index_in_face(hd, tm); const int index_of_opp_hd = halfedge_index_in_face(opp_hd, tm); // - Coordinates will be non-null at indices `index_of_hd` // and `index_of_hd + 1` in loc.first. // - Coordinates will be non-null at indices `index_of_opp_hd` // and `index_of_opp_hd + 1` in f. // - The halfedges `hd` and `opp_hd` have opposite directions. loc_in_fd.second[index_of_opp_hd] = loc.second[(index_of_hd + 1)%3]; loc_in_fd.second[(index_of_opp_hd + 1)%3] = loc.second[index_of_hd]; // note that the barycentric coordinates were initialized at 0, // so the third coordinate is already set up properly } else { CGAL_assertion_code(const face_descriptor fd2 = boost::get(dv);) CGAL_assertion(fd2 != boost::graph_traits::null_face()); CGAL_assertion(fd2 != fd); // Calling this function for a location that is (strictly) in a face but // asking for the location in a nearby face is meaningless CGAL_assertion(false); } CGAL_postcondition(loc_in_fd.first == fd); return loc_in_fd; } // not documenting the next two functions as they are too technical #ifndef DOXYGEN_RUNNING // Finding a common face to a location and a point // - the first location must be known // - the second must be a point in a face incident to get_descriptor_from_location(known_location) // note: not returning the query location to emphasis that the known location can change too. template bool locate_in_common_face(std::pair::face_descriptor, std::array >& known_location, const typename internal::Location_traits::Point& query, std::pair::face_descriptor, std::array >& query_location, const TriangleMesh& tm, const NamedParameters& np, const FT tolerance = std::numeric_limits::epsilon()) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::face_descriptor face_descriptor; descriptor_variant dv = get_descriptor_from_location(known_location, tm); bool is_query_location_in_face = false; if(const vertex_descriptor* vd_ptr = boost::get(&dv)) { const vertex_descriptor vd = *vd_ptr; halfedge_descriptor hd = halfedge(vd, tm); for(face_descriptor fd : faces_around_target(hd, tm)) { if(fd == boost::graph_traits::null_face()) continue; // check if 'query' can be found in that face query_location = locate_in_face(query, fd, tm, np); internal::snap_location_to_border(query_location, tm, tolerance); // @tmp keep or not ? is_query_location_in_face = is_in_face(query_location, tm); if(is_query_location_in_face) break; } } else if(const halfedge_descriptor* hd_ptr = boost::get(&dv)) { const halfedge_descriptor hd = *hd_ptr; face_descriptor fd = face(hd, tm); if(fd != boost::graph_traits::null_face()) { query_location = locate_in_face(query, fd, tm, np); internal::snap_location_to_border(query_location, tm, tolerance); // @tmp keep or not ? is_query_location_in_face = is_in_face(query_location, tm); } if(!is_query_location_in_face) { fd = face(opposite(hd, tm), tm); query_location = locate_in_face(query, fd, tm, np); is_query_location_in_face = is_in_face(query_location, tm); } } else { const face_descriptor fd = boost::get(dv); CGAL_precondition(fd != boost::graph_traits::null_face()); query_location = locate_in_face(query, fd, tm, np); internal::snap_location_to_border(query_location, tm, tolerance); // @tmp keep or not ? is_query_location_in_face = is_in_face(query_location, tm); } // if this is not the same face as for 'known_query', change 'known_location' if(is_query_location_in_face && query_location.first != known_location.first) known_location = locate_in_adjacent_face(known_location, query_location.first, tm); return is_query_location_in_face; } // Finding a common face to two locations // - both locations must be known but can change template bool locate_in_common_face(std::pair::face_descriptor, std::array >& first_location, std::pair::face_descriptor, std::array >& second_location, const TriangleMesh& tm) { typedef typename boost::graph_traits::face_descriptor face_descriptor; // Check that we actually have something to do if(first_location.first == second_location.first) return true; bool is_first_location_on_border = is_on_face_border(first_location, tm); bool is_second_location_on_border = is_on_face_border(second_location, tm); // We have already checked that they have different faces, if neither are on // a border, then it's hopeless if(!is_first_location_on_border && !is_second_location_on_border) return false; // Find a common face in the sets of incident faces of each location std::set first_incident_faces; std::set second_incident_faces; internal::incident_faces(first_location, tm, std::inserter(first_incident_faces, first_incident_faces.begin())); internal::incident_faces(second_location, tm, std::inserter(second_incident_faces, second_incident_faces.begin())); typename std::set::const_iterator fit = first_incident_faces.begin(); typename std::set::const_iterator fend = first_incident_faces.end(); typename std::set::const_iterator sit = second_incident_faces.begin(); typename std::set::const_iterator send = second_incident_faces.end(); while(fit!=fend && sit!=send) { if(*fit == boost::graph_traits::null_face()) ++fit; if(*sit == boost::graph_traits::null_face()) ++sit; if(*fit == *sit) break; else if(*fit < *sit) ++fit; else ++sit; } if(fit == fend || sit == send) // no common face... return false; CGAL_assertion(*fit == *sit); face_descriptor common_fd = *fit; CGAL_assertion(common_fd != boost::graph_traits::null_face()); if(first_location.first != common_fd) first_location = locate_in_adjacent_face(first_location, common_fd, tm); if(second_location.first != common_fd) second_location = locate_in_adjacent_face(second_location, common_fd, tm); CGAL_postcondition(first_location.first == second_location.first); return true; } #endif // DOXYGEN_RUNNING /// @} namespace internal { template ::value> struct Point_to_Point_3 // 2D case { typedef typename GetGeomTraits::type::Point_3 Point_3; Point_3 operator()(const Point& p) const { return Point_3(p.x(), p.y(), 0); } }; template struct Point_to_Point_3::type::Point_3, 3> // 3D case with nothing to do { typedef typename GetGeomTraits::type::Point_3 Point_3; const Point_3& operator()(const Point_3& p) const { return p; } }; template struct Point_to_Point_3 // Generic 3D case { typedef typename GetGeomTraits::type::Point_3 Point_3; Point_3 operator()(const Point& p) const { return Point_3(p.x(), p.y(), p.z()); } }; template struct Ray_to_Ray_3 // 2D case { typedef typename GetGeomTraits::type Geom_traits; typedef typename Geom_traits::Ray_2 Ray_2; typedef typename Geom_traits::Ray_3 Ray_3; typedef Point_to_Point_3 P2_to_P3; Ray_3 operator()(const Ray_2& r) const { P2_to_P3 to_p3; return Ray_3(to_p3(r.source()), to_p3(r.second_point())); } const Ray_3& operator()(const Ray_3& r) const { return r; } }; // Readable property map that converts the output of a given vertex point map to a 3D point template ::const_type> struct Point_to_Point_3_VPM { private: typedef VertexPointMap VPM; typedef Point_to_Point_3_VPM Self; public: typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::property_traits::value_type Point; typedef Point_to_Point_3 P_to_P3; typedef typename CGAL::Kernel_traits::Kernel K; typedef typename K::Point_3 Point_3; // required typedefs typedef typename boost::property_traits::key_type key_type; typedef Point_3 value_type; typedef value_type reference; typedef boost::readable_property_map_tag category; // Constructors Point_to_Point_3_VPM() : conv_(), vpm_() { } // required for compilation by AABBtraits Point_to_Point_3_VPM(const VertexPointMap vpm) : conv_(), vpm_(vpm) { } Point_to_Point_3_VPM(const TriangleMesh& mesh) : conv_(), vpm_(get_const_property_map(boost::vertex_point, mesh)) { } // Access const P_to_P3& converter() const { return conv_; } const VertexPointMap& vpm() const { return vpm_; } // get function for property map inline friend reference get(const Self& pmap, key_type v) { return pmap.converter()(get(pmap.vpm(), v)); } private: // Can't be const nor references due to AABB_traits, so make sure to use property maps! P_to_P3 conv_; VertexPointMap vpm_; }; // Two different functions, because the AABB's traits' VPM must match the passed VPM (that is, // the original VPM wrapped with P_to_P3_VPM if the VPM's value_type was not Point_3) template void build_AABB_tree(const TriangleMesh& tm, AABB_tree& outTree, const VPM& wrapped_vpm, typename std::enable_if< std::is_same< typename AABBTraits::Point_3, typename boost::property_traits::value_type >::value>::type* = 0) { typename boost::graph_traits::face_iterator ffirst, fbeyond; boost::tie(ffirst, fbeyond) = faces(tm); outTree.rebuild(ffirst, fbeyond, tm, wrapped_vpm); outTree.build(); } template void build_AABB_tree(const TriangleMesh& tm, AABB_tree& outTree, const VPM& vpm, typename std::enable_if< !std::is_same< typename AABBTraits::Point_3, typename boost::property_traits::value_type >::value>::type* = 0) { typedef internal::Point_to_Point_3_VPM Wrapped_VPM; const Wrapped_VPM wrapped_vpm(vpm); return internal::build_AABB_tree(tm, outTree, wrapped_vpm); } } // namespace internal /// \name Nearest Face Location Queries /// The following functions can be used to find the closest point on a triangle mesh, given either /// a point or a ray. This closest point is computed using a `CGAL::AABB_tree`. Users intending /// to call location functions on more than a single point (or ray) should first compute an AABB /// tree to store it (otherwise, it will be recomputed every time). Note that since the AABB tree /// class is a 3D structure, it might be required to wrap your point property map to convert your /// point type to the 3D point type (i.e., your traits' `%Point_3`) if you are working /// with a 2D triangle structure. /// /// @{ /// \ingroup PMP_locate_grp /// /// \brief creates an AABB tree suitable for use with `locate_with_AABB_tree()`. /// /// \details This function should first be called by users who intend to locate multiple points: /// in this case, it is better to first build an AABB tree, and use the function /// `locate_with_AABB_tree()` that takes as parameter an AABB tree, instead of calling `locate()` /// multiple times, which will build a new AABB tree on every call. /// /// \tparam TriangleMesh must be a model of `FaceListGraph` /// \tparam Point3VPM must be a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and the \cgal 3D point type (your traits' `%Point_3`) as value type. /// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" /// /// \param tm a triangulated surface mesh /// \param outTree output parameter that stores the computed `AABB_tree` /// \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below /// /// \cgalNamedParamsBegin /// \cgalParamNBegin{vertex_point_map} /// \cgalParamDescription{a property map associating points to the vertices of `tm`} /// \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and `%Point_3` as value type} /// \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} /// \cgalParamNEnd /// /// \cgalParamNBegin{geom_traits} /// \cgalParamDescription{an instance of a geometric traits class} /// \cgalParamType{a class model of `Kernel`} /// \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} /// \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} /// \cgalParamExtra{Must be identical to the traits used in the template parameter of the `AABB_traits`.} /// \cgalParamNEnd /// \cgalNamedParamsEnd /// template void build_AABB_tree(const TriangleMesh& tm, AABB_tree< AABB_traits< #ifdef DOXYGEN_RUNNING Geom_traits, #else typename GetGeomTraits::type, #endif CGAL::AABB_face_graph_triangle_primitive > >& outTree, const NamedParameters& np) { typedef typename GetVertexPointMap::const_type VertexPointMap; using parameters::get_parameter; using parameters::choose_parameter; const VertexPointMap vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), get_const_property_map(boost::vertex_point, tm)); return internal::build_AABB_tree(tm, outTree, vpm); } #ifndef DOXYGEN_RUNNING template void build_AABB_tree(const TriangleMesh& tm, AABB_tree& outTree) { return build_AABB_tree(tm, outTree, parameters::all_default()); } #endif /// \ingroup PMP_locate_grp /// /// \brief returns the face location nearest to the given point, as a location. /// /// Note that it is possible for the triangle mesh to have ambiant dimension `2` (e.g. the mesh /// is a 2D triangulation, or a CGAL::Surface_mesh >), as long as an appropriate /// vertex point property map is passed in the AABB tree, which will convert from 2D to 3D. /// /// \tparam TriangleMesh must be a model of `FaceListGraph` /// \tparam Point3VPM must be a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and the \cgal 3D point type (your traits' `%Point_3`) as value type. /// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" /// /// \param p the point to locate on the input triangulated surface mesh /// \param tree an AABB tree containing the triangular faces of the input surface mesh to perform the point location with /// \param tm a triangulated surface mesh /// \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below /// /// \cgalNamedParamsBegin /// \cgalParamNBegin{vertex_point_map} /// \cgalParamDescription{a property map associating points to the vertices of `tm`} /// \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and `%Point_3` as value type} /// \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} /// \cgalParamNEnd /// /// \cgalParamNBegin{geom_traits} /// \cgalParamDescription{an instance of a geometric traits class} /// \cgalParamType{a class model of `Kernel`} /// \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} /// \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} /// \cgalParamExtra{Must be identical to the traits used in the template parameter of the `AABB_traits`.} /// \cgalParamNEnd /// /// \cgalParamNBegin{snapping_tolerance} /// \cgalParamDescription{a tolerance value used to snap barycentric coordinates} /// \cgalParamType{double} /// \cgalParamDefault{`0`} /// \cgalParamExtra{Depending on the geometric traits used, the computation of the barycentric coordinates /// might be an inexact construction, thus leading to sometimes surprising values /// (e.g. a triplet `[0.5, 0.5, -1-e17]` for a point at the middle of an edge). /// The coordinates will be snapped towards `0` and `1` if the difference is smaller /// than the tolerance value, while still ensuring that the total sum of the coordinates is `1`.} /// \cgalParamNEnd /// \cgalNamedParamsEnd /// /// \returns a face location. The type `FT` is deduced from the geometric traits, either provided by /// the user via named parameters (with `geom_traits`) or using `CGAL::Kernel_traits` /// and the point type of the vertex point property map in use. /// template #ifdef DOXYGEN_RUNNING Face_location locate_with_AABB_tree(const Point& p, const AABB_tree > >& tree, #else typename internal::Location_traits::Face_location locate_with_AABB_tree(const typename internal::Location_traits::Point& p, const AABB_tree::type, CGAL::AABB_face_graph_triangle_primitive > >& tree, #endif const TriangleMesh& tm, const NamedParameters& np) { typedef typename internal::Location_traits::Point Point; typedef internal::Point_to_Point_3 P_to_P3; typedef typename boost::property_traits::value_type Point_3; CGAL_static_assertion((std::is_same::value)); typedef typename GetGeomTraits::type Geom_traits; typedef typename CGAL::AABB_face_graph_triangle_primitive Primitive; typedef typename CGAL::AABB_traits AABB_traits; typedef typename GetVertexPointMap::const_type VertexPointMap; typedef internal::Point_to_Point_3_VPM WrappedVPM; const Point_3& p3 = P_to_P3()(p); typename AABB_tree::Point_and_primitive_id result = tree.closest_point_and_primitive(p3); typedef typename GetGeomTraits::type Geom_traits; using parameters::get_parameter; using parameters::choose_parameter; // The VPM might return a point of any dimension, but the AABB tree necl1671essarily returns // a Point_3. So, wrap the VPM (again) to give a Point_3. Even if it's already wrapped, we're just // forwarding a const& anyway. const VertexPointMap vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), get_const_property_map(boost::vertex_point, tm)); const WrappedVPM wrapped_vpm(vpm); return locate_in_face(result.first, result.second, tm, CGAL::parameters::vertex_point_map(wrapped_vpm)); } #ifndef DOXYGEN_RUNNING template typename internal::Location_traits::Face_location locate_with_AABB_tree(const typename internal::Location_traits::Point& p, const AABB_tree& tree, const TriangleMesh& tm) { return locate_with_AABB_tree(p, tree, tm, parameters::all_default()); } #endif /// \ingroup PMP_locate_grp /// /// \brief returns the nearest face location to the given point. /// /// \details Note that this function will build an AABB tree on each call. If you need /// to call this function more than once, first use `build_AABB_tree()` to create a /// an AABB tree that you can store and use the function `locate_with_AABB_tree()`. /// /// \tparam TriangleMesh must be a model of `FaceListGraph`. /// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" /// /// \param p the point to locate on the input triangulated surface mesh /// \param tm a triangulated surface mesh /// \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below /// /// \cgalNamedParamsBegin /// \cgalParamNBegin{vertex_point_map} /// \cgalParamDescription{a property map associating points to the vertices of `tm`} /// \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and `%Point_3` as value type} /// \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} /// \cgalParamNEnd /// /// \cgalParamNBegin{geom_traits} /// \cgalParamDescription{an instance of a geometric traits class} /// \cgalParamType{a class model of `Kernel`} /// \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} /// \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} /// \cgalParamNEnd /// /// \cgalParamNBegin{snapping_tolerance} /// \cgalParamDescription{a tolerance value used to snap barycentric coordinates} /// \cgalParamType{double} /// \cgalParamDefault{`0`} /// \cgalParamExtra{Depending on the geometric traits used, the computation of the barycentric coordinates /// might be an inexact construction, thus leading to sometimes surprising values /// (e.g. a triplet `[0.5, 0.5, -1-e17]` for a point at the middle of an edge). /// The coordinates will be snapped towards `0` and `1` if the difference is smaller /// than the tolerance value, while still ensuring that the total sum of the coordinates is `1`.} /// \cgalParamNEnd /// \cgalNamedParamsEnd /// template #ifdef DOXYGEN_RUNNING Face_location locate(const Point& p, #else typename internal::Location_traits::Face_location locate(const typename internal::Location_traits::Point& p, #endif const TriangleMesh& tm, const NamedParameters& np) { // Wrap the input VPM with a one converting to 3D (costs nothing if the input VPM // already has value type Kernel::Point_3) typedef typename GetVertexPointMap::const_type VertexPointMap; typedef internal::Point_to_Point_3_VPM WrappedVPM; typedef typename internal::Location_traits::Point Intrinsic_point; typedef typename GetGeomTraits::type Geom_traits; typedef AABB_face_graph_triangle_primitive AABB_face_graph_primitive; typedef CGAL::AABB_traits AABB_face_graph_traits; typedef internal::Point_to_Point_3 P_to_P3; typedef typename AABB_face_graph_traits::Point_3 Point_3; using parameters::get_parameter; using parameters::choose_parameter; CGAL_static_assertion((std::is_same::value)); const VertexPointMap vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), get_const_property_map(boost::vertex_point, tm)); const WrappedVPM wrapped_vpm(vpm); AABB_tree tree; build_AABB_tree(tm, tree, parameters::vertex_point_map(wrapped_vpm)); const Point_3& p3 = P_to_P3()(p); return locate_with_AABB_tree(p3, tree, tm, parameters::vertex_point_map(wrapped_vpm)); } #ifndef DOXYGEN_RUNNING template typename internal::Location_traits::Face_location locate(const typename property_map_value::type& p, const TriangleMesh& tm) { return locate(p, tm, parameters::all_default()); } #endif /// \ingroup PMP_locate_grp /// /// \brief returns the face location along `ray` nearest to its source point. /// /// If the ray does not intersect the mesh, a default constructed location is returned. /// /// \tparam TriangleMesh must be a model of `FaceListGraph`. /// \tparam Point3VPM must be a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and the \cgal 3D point type (your traits' `%Point_3`) as value type. /// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" /// /// \param ray a ray to intersect with the input triangulated surface mesh /// \param tree an AABB tree containing the triangular faces of the input surface mesh to perform the point location with /// \param tm a triangulated surface mesh /// \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below /// /// \cgalNamedParamsBegin /// \cgalParamNBegin{vertex_point_map} /// \cgalParamDescription{a property map associating points to the vertices of `tm`} /// \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and `%Point_3` as value type} /// \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} /// \cgalParamNEnd /// /// \cgalParamNBegin{geom_traits} /// \cgalParamDescription{an instance of a geometric traits class} /// \cgalParamType{a class model of `Kernel`} /// \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} /// \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} /// \cgalParamExtra{Must be identical to the traits used in the template parameter of the `AABB_traits`.} /// \cgalParamNEnd /// /// \cgalParamNBegin{snapping_tolerance} /// \cgalParamDescription{a tolerance value used to snap barycentric coordinates} /// \cgalParamType{double} /// \cgalParamDefault{`0`} /// \cgalParamExtra{Depending on the geometric traits used, the computation of the barycentric coordinates /// might be an inexact construction, thus leading to sometimes surprising values /// (e.g. a triplet `[0.5, 0.5, -1-e17]` for a point at the middle of an edge). /// The coordinates will be snapped towards `0` and `1` if the difference is smaller /// than the tolerance value, while still ensuring that the total sum of the coordinates is `1`.} /// \cgalParamNEnd /// \cgalNamedParamsEnd /// /// \pre `ray` is an object with the same ambient dimension as the point type (the value type of the vertex point map). /// template #ifdef DOXYGEN_RUNNING Face_location locate_with_AABB_tree(const Ray& ray, const AABB_tree > >& tree, #else typename internal::Location_traits::Face_location locate_with_AABB_tree(const typename internal::Location_traits::Ray& ray, const AABB_tree< CGAL::AABB_traits< typename GetGeomTraits::type, CGAL::AABB_face_graph_triangle_primitive > >& tree, #endif const TriangleMesh& tm, const NamedParameters& np) { typedef typename GetGeomTraits::type Geom_traits; typedef typename Geom_traits::FT FT; typedef typename Geom_traits::Point_3 Point_3; typedef typename Geom_traits::Ray_3 Ray_3; typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef typename GetVertexPointMap::const_type VertexPointMap; typedef internal::Point_to_Point_3_VPM WrappedVPM; typedef internal::Ray_to_Ray_3 R_to_R3; typedef typename CGAL::AABB_face_graph_triangle_primitive Primitive; typedef typename CGAL::AABB_traits AABB_traits; typedef AABB_tree AABB_face_graph_tree; typedef typename AABB_face_graph_tree::template Intersection_and_primitive_id::Type Intersection_type; typedef boost::optional Ray_intersection; using parameters::get_parameter; using parameters::choose_parameter; // First, transform the ray into a 3D ray if needed Ray_3 ray_3 = R_to_R3()(ray); std::vector intersections; tree.all_intersections(ray_3, std::back_inserter(intersections)); bool found = false; FT nearest_distance = 0; Point_3 nearest_point = CGAL::ORIGIN; face_descriptor nearest_face; for(std::size_t i = 0; i < intersections.size(); ++i) { if(intersections[i]) { Point_3* intersection_point = boost::get(&(intersections[i]->first)); if(intersection_point) { FT distance = CGAL::squared_distance(*intersection_point, ray_3.source()); if(!found || distance < nearest_distance) { found = true; nearest_point = *intersection_point; nearest_distance = distance; nearest_face = intersections[i]->second; } } } } if(found) { // wrap the VPM to make sure it is producing 3D points const VertexPointMap vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), get_const_property_map(boost::vertex_point, tm)); WrappedVPM wrapped_vpm(vpm); return locate_in_face(nearest_point, nearest_face, tm, CGAL::parameters::vertex_point_map(wrapped_vpm)); } else return std::make_pair(boost::graph_traits::null_face(), CGAL::make_array(FT(0), FT(0), FT(0))); } #ifndef DOXYGEN_RUNNING template typename internal::Location_traits::Face_location locate_with_AABB_tree(const typename internal::Location_traits::Ray& ray, const AABB_tree& tree, const TriangleMesh& tm) { return locate_with_AABB_tree(ray, tree, tm, parameters::all_default()); } #endif /// \ingroup PMP_locate_grp /// /// \brief returns the face location along `ray` nearest to its source point. /// /// If the ray does not intersect the mesh, a default constructed location is returned. /// /// \details Note that this function will build an AABB tree on each call. If you need /// to call this function more than once, use `build_AABB_tree()` to cache a /// copy of the `AABB_tree`, and use the overloads of this function /// that accept a reference to an AABB tree as input. /// /// \tparam TriangleMesh must be a model of `FaceListGraph`. /// \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" /// /// \param ray a ray to intersect with the input triangulated surface mesh /// \param tm the input triangulated surface mesh /// \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below /// /// \cgalNamedParamsBegin /// \cgalParamNBegin{vertex_point_map} /// \cgalParamDescription{a property map associating points to the vertices of `tm`} /// \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` /// as key type and `%Point_3` as value type} /// \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} /// \cgalParamNEnd /// /// \cgalParamNBegin{geom_traits} /// \cgalParamDescription{an instance of a geometric traits class} /// \cgalParamType{a class model of `Kernel`} /// \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} /// \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} /// \cgalParamNEnd /// /// \cgalParamNBegin{snapping_tolerance} /// \cgalParamDescription{a tolerance value used to snap barycentric coordinates} /// \cgalParamType{double} /// \cgalParamDefault{`0`} /// \cgalParamExtra{Depending on the geometric traits used, the computation of the barycentric coordinates /// might be an inexact construction, thus leading to sometimes surprising values /// (e.g. a triplet `[0.5, 0.5, -1-e17]` for a point at the middle of an edge). /// The coordinates will be snapped towards `0` and `1` if the difference is smaller /// than the tolerance value, while still ensuring that the total sum of the coordinates is `1`.} /// \cgalParamNEnd /// \cgalNamedParamsEnd /// /// \pre `ray` is an object with the same ambient dimension as the point type (the value type of the vertex point map). /// template #ifdef DOXYGEN_RUNNING Face_location locate(const Ray& ray, #else typename internal::Location_traits::Face_location locate(const typename internal::Location_traits::Ray& ray, #endif const TriangleMesh& tm, const NamedParameters& np) { typedef typename GetVertexPointMap::const_type VertexPointMap; // Wrap the input VPM with a one converting to 3D (costs nothing if the input VPM // already has value type Geom_traits::Point_3) typedef internal::Point_to_Point_3_VPM VPM; typedef typename GetGeomTraits::type Geom_traits; typedef AABB_face_graph_triangle_primitive AABB_face_graph_primitive; typedef CGAL::AABB_traits AABB_face_graph_traits; using parameters::get_parameter; using parameters::choose_parameter; const VertexPointMap vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point), get_const_property_map(boost::vertex_point, tm)); const VPM wrapped_vpm(vpm); AABB_tree tree; build_AABB_tree(tm, tree, parameters::vertex_point_map(wrapped_vpm)); return locate_with_AABB_tree(ray, tree, tm, np); } #ifndef DOXYGEN_RUNNING template typename internal::Location_traits::Face_location locate(const typename internal::Ray_type_selector< typename internal::Location_traits::Point>::type& ray, const TriangleMesh& tm) { return locate(ray, tm, parameters::all_default()); } #endif /// @} } // namespace Polygon_mesh_processing } // namespace CGAL #endif // CGAL_POLYGON_MESH_PROCESSING_LOCATE_H