cgal/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/locate.h

1846 lines
73 KiB
C++

// Copyright (c) 2014, 2017, 2018 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$
// SPDX-License-Identifier: GPL-3.0+
//
// Author(s) : Mael Rouxel-Labbé,
// Stephen Kiazyk
//
#ifndef CGAL_POLYGON_MESH_PROCESSING_LOCATE_H
#define CGAL_POLYGON_MESH_PROCESSING_LOCATE_H
#include <CGAL/license/Polygon_mesh_processing/locate.h>
#include <CGAL/AABB_face_graph_triangle_primitive.h>
#include <CGAL/AABB_traits.h>
#include <CGAL/AABB_tree.h>
#include <CGAL/boost/graph/helpers.h>
#include <CGAL/boost/graph/iterator.h>
#include <CGAL/Default.h>
#include <CGAL/Kernel_traits.h>
#include <CGAL/Kernel/global_functions.h>
#include <CGAL/number_utils.h>
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
#include <CGAL/Random.h>
#include <CGAL/use.h>
#include <boost/graph/graph_traits.hpp>
#include <boost/mpl/if.hpp>
#include <boost/variant.hpp>
#include <array>
#include <iostream>
#include <iterator>
#include <limits>
#include <set>
#include <type_traits>
#include <utility>
#include <vector>
// Everywhere in this file:
// If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`)
// such that `bc` is `(w0, w1, w2)`, the correspondance with the weights 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 <typename Point,
int dim = CGAL::Ambient_dimension<Point>::value>
struct Ray_type_selector
{
typedef typename CGAL::Kernel_traits<Point>::type Kernel;
typedef typename Kernel::Ray_2 type;
};
template<typename Point>
struct Ray_type_selector<Point, 3>
{
typedef typename CGAL::Kernel_traits<Point>::type Kernel;
typedef typename Kernel::Ray_3 type;
};
} // end namespace internal
/// \ingroup PMP_locate_grp
///
/// \brief Helper class whose sole purpose is to make it easier to access some useful types.
///
/// \tparam PolygonMesh A model of `FaceListGraph`
/// \tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
template <typename PolygonMesh,
typename NamedParameters = Default>
class Locate_types
{
public:
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<PolygonMesh>::face_descriptor face_descriptor;
typedef boost::variant<vertex_descriptor,
halfedge_descriptor,
face_descriptor> descriptor_variant;
#ifdef DOXYGEN_RUNNING
/// This is the type of the vertex point property map, either the one passed through the named parameters
/// or the default, internal one of the mesh.
typedef unspecified_type VPM;
/// The traits class, either passed as a named parameter or deduced from the Point type
/// of the vertex property map (the point type must then be compatible with `CGAL::Kernel_traits`)
typedef unspecified_type Geom_traits;
#else
typedef typename GetVertexPointMap<PolygonMesh,
NamedParameters>::const_type VPM;
typedef typename GetGeomTraits<PolygonMesh, NamedParameters>::type Geom_traits;
#endif
typedef typename boost::property_traits<VPM>::value_type Point;
typedef typename Geom_traits::FT FT;
#ifdef DOXYGEN_RUNNING
/// Either Geom_traits::Ray_2 or Geom_traits::Ray_3, depending on the ambient dimension of the mesh
typedef unspecified_type Ray;
#else
typedef typename internal::Ray_type_selector<Point>::type Ray;
#endif
typedef std::array<FT, 3> Barycentric_coordinates;
/// If `tm` is the input triangulated surface mesh and given the pair (`f`, `bc`)
/// such that `bc` is `(w0, w1, w2)`, the correspondance with the weights 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)`
typedef std::pair<face_descriptor, Barycentric_coordinates> Face_location;
};
// forward declarations
template <typename TriangleMesh>
bool is_in_face(const typename Locate_types<TriangleMesh>::Face_location& loc,
const TriangleMesh& tm);
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::descriptor_variant
get_descriptor_from_location(const typename Locate_types<TriangleMesh>::Face_location& loc,
const TriangleMesh& tm);
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
locate_in_face(typename boost::graph_traits<TriangleMesh>::halfedge_descriptor he,
typename Locate_types<TriangleMesh>::FT t,
const TriangleMesh& tm);
// end of forward declarations
namespace internal {
template<typename TriangleMesh, typename OutputIterator>
OutputIterator
incident_faces(const typename Locate_types<TriangleMesh>::Face_location& location,
const TriangleMesh& tm,
OutputIterator out)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef boost::variant<vertex_descriptor,
halfedge_descriptor,
face_descriptor> descriptor_variant;
descriptor_variant dv = get_descriptor_from_location(location, tm);
if(const vertex_descriptor* vd_ptr = boost::get<vertex_descriptor>(&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<halfedge_descriptor>(&dv))
{
const halfedge_descriptor hd = *hd_ptr;
*out++ = face(hd, tm);
*out++ = face(opposite(hd, tm), tm);
}
else
{
const face_descriptor fd = boost::get<face_descriptor>(dv);
*out++ = fd;
}
return out;
}
// Snapping coordinates for robustness
template<typename TriangleMesh>
bool
snap_coordinates_to_border(typename Locate_types<TriangleMesh>::Barycentric_coordinates& coords,
const typename Locate_types<TriangleMesh>::FT tolerance =
std::numeric_limits<typename Locate_types<TriangleMesh>::FT>::epsilon())
{
typedef typename Locate_types<TriangleMesh>::FT FT;
#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] = 0.;
}
else if(CGAL::abs(1 - coords[i]) <= tolerance)
{
snapped = true;
residue -= 1. - coords[i];
coords[i] = 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] != 0. && coords[i] != 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<typename TriangleMesh>
bool
snap_location_to_border(typename Locate_types<TriangleMesh>::Face_location& loc,
const typename Locate_types<TriangleMesh>::FT tolerance =
std::numeric_limits<typename Locate_types<TriangleMesh>::FT>::epsilon())
{
return snap_coordinates_to_border<TriangleMesh>(loc.second, tolerance);
}
template <typename K, typename P, int = P::Ambient_dimension::value>
struct Barycentric_coordinate_calculator // 2D version
{
CGAL::array<typename K::FT, 3>
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 <typename K, typename P>
struct Barycentric_coordinate_calculator<K, P, 3 /*3D specialization*/>
{
CGAL::array<typename K::FT, 3>
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 = 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 <typename K, typename P, int = P::Ambient_dimension::value>
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 != 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 <typename K, typename P>
struct Barycentric_point_constructor<K, P, 3> // 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 != 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 P the type of a geometric 2D or 3D point
/// \tparam K the type of a geometric traits. Must be a model of `Kernel`.
///
/// \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 k an instance of the geometric traits
///
/// \pre `p`, `q`, and `r` are not collinear.
/// \pre It must be possible to extract a kernel type model of `Kernel`, using `CGAL::Kernel_traits<P>`
/// (this is the case for all standard %CGAL point types).
/// \pre `query` lies on the plane defined by `p`, `q`, and `r`.
///
template <typename K, typename P>
CGAL::array<typename K::FT, 3>
barycentric_coordinates(const P& p, const P& q, const P& r, const P& query, const K& k)
{
internal::Barycentric_coordinate_calculator<K, P> calculator;
return calculator(p, q, r, query, k);
}
template <typename P>
CGAL::array<typename CGAL::Kernel_traits<P>::type::FT, 3>
barycentric_coordinates(const P& p, const P& q, const P& r, const P& query)
{
typedef typename CGAL::Kernel_traits<P>::type Kernel;
return barycentric_coordinates<Kernel, P>(p, q, r, query, Kernel());
}
/// \name Random Location Generation
/// @{
/// \ingroup PMP_locate_grp
///
/// \brief Returns a random point over the halfedge `hd`, as a `Face_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 TriangleMesh A model of `FaceGraph`
///
/// \param hd a halfedge of `tm`
/// \param tm a triangulated surface mesh
/// \param rnd optional random number generator
///
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
random_location_on_halfedge(typename boost::graph_traits<TriangleMesh>::halfedge_descriptor hd,
const TriangleMesh& tm,
CGAL::Random& rnd = get_default_random())
{
typedef typename Locate_types<TriangleMesh>::FT FT;
CGAL_precondition(CGAL::is_triangle_mesh(tm));
FT t(rnd.uniform_real(0., 1.));
return locate_in_face(hd, t, tm);
}
/// \ingroup PMP_locate_grp
///
/// \brief Returns a random point over the face `fd`, as a `Face_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 TriangleMesh A model of `FaceGraph`
///
/// \param fd a face of `tm`
/// \param tm a triangulated surface mesh
/// \param rnd optional random number generator
///
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
random_location_on_face(typename boost::graph_traits<TriangleMesh>::face_descriptor fd,
const TriangleMesh& tm,
CGAL::Random& rnd = get_default_random())
{
typedef typename Locate_types<TriangleMesh>::FT FT;
CGAL_USE(tm);
CGAL_precondition(CGAL::is_triangle_mesh(tm));
CGAL_precondition(fd != boost::graph_traits<TriangleMesh>::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 TriangleMesh A model of `FaceGraph`
///
/// \param tm a triangulated surface mesh
/// \param rnd optional random number generator
///
/// \sa `random_location_on_face()`
///
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
random_location_on_mesh(const TriangleMesh& tm, CGAL::Random& rnd = get_default_random())
{
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
CGAL_precondition(CGAL::is_triangle_mesh(tm));
face_descriptor fd = random_face_in_mesh(tm, rnd);
return random_location_on_face(fd, tm, rnd);
}
/// @}
/// \ingroup PMP_locate_grp
///
/// \brief Given a `Face_location`, that is an ordered pair composed of a
/// `boost::graph_traits<TriangleMesh>::%face_descriptor` and an array
/// of barycentric coordinates, 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 `vertex_descriptor` `v`;
/// - if the point lies on a halfedge, this function returns a `halfedge_descriptor` `hd`
/// (note that in that case, `loc.first == face(hd, tm)` holds).
/// - otherwise, this function returns a `boost::graph_traits<TriangleMesh>face_descriptor`
/// `fd` (equal to `loc.first`).
///
/// \tparam TriangleMesh A model of `FaceGraph`
///
/// \pre the location corresponds to a point that is within a face of `tm`.
/// \pre `tm` is a triangulated surface mesh.
///
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::descriptor_variant
get_descriptor_from_location(const typename Locate_types<TriangleMesh>::Face_location& loc,
const TriangleMesh& tm)
{
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef typename Locate_types<TriangleMesh>::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<TriangleMesh>::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] == 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] == 0) // coordinate at target(hd, tm)
return prev(hd, tm);
hd = next(hd, tm);
}
return fd;
}
/// \ingroup PMP_locate_grp
///
/// \brief Given a `Face_location`, that is an ordered pair composed of a
/// `boost::graph_traits<TriangleMesh>::%face_descriptor` and an array
/// of barycentric coordinates, returns the geometric position described
/// by these coordinates, as a point.
///
/// \tparam TriangleMesh A model of `FaceGraph`
/// \tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// \param loc the location to transform into a point
/// \param tm a triangulated surface mesh
/// \param np an optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `tm`.
/// If this parameter is omitted, an internal property map for
/// `boost::vertex_point_t` should be available in `TriangleMesh`.
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits}
/// a geometric traits class instance, model of `Kernel`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \pre `loc.first` is a face descriptor corresponding to a face of `tm`.
/// \pre `tm` is a triangulated surface mesh.
///
template <typename TriangleMesh, typename NamedParameters>
typename Locate_types<TriangleMesh, NamedParameters>::Point
construct_point(const typename Locate_types<TriangleMesh>::Face_location& loc,
const TriangleMesh& tm,
const NamedParameters& np)
{
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
typedef typename boost::property_traits<VertexPointMap>::value_type Point;
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type Geom_traits;
CGAL_precondition(CGAL::is_triangle_mesh(tm));
VertexPointMap vpm = boost::choose_param(boost::get_param(np, internal_np::vertex_point),
get_const_property_map(boost::vertex_point, tm));
Geom_traits gt = choose_param(get_param(np, internal_np::geom_traits), Geom_traits());
halfedge_descriptor hd = halfedge(loc.first, tm);
const Point& p0 = get(vpm, source(hd, tm));
const Point& p1 = get(vpm, target(hd, tm));
const Point& p2 = get(vpm, target(next(hd, tm), tm));
internal::Barycentric_point_constructor<Geom_traits, Point> bp_constructor;
return bp_constructor(p0, loc.second[0], p1, loc.second[1], p2, loc.second[2], gt);
}
template <typename TriangleMesh>
typename property_map_value<TriangleMesh, boost::vertex_point_t>::type
construct_point(const typename Locate_types<TriangleMesh>::Face_location& loc,
const TriangleMesh& tm)
{
return construct_point(loc, tm, parameters::all_default());
}
/// \name Location Predicates
/// @{
/// \ingroup PMP_locate_grp
///
/// \brief Given a `Face_location`, that is an ordered pair composed of a
/// `boost::graph_traits<TriangleMesh>::%face_descriptor` and an array
/// of barycentric coordinates, 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 `(w0, w1, w2)`, the correspondance with the weights 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 A model of `FaceGraph`
///
/// \pre `loc.first` is a face descriptor corresponding to a face of `tm`.
/// \pre `tm` is a triangulated surface mesh.
///
template <typename TriangleMesh>
bool
is_on_vertex(const typename Locate_types<TriangleMesh>::Face_location& loc,
const typename boost::graph_traits<TriangleMesh>::vertex_descriptor v,
const TriangleMesh& tm)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename Locate_types<TriangleMesh>::descriptor_variant descriptor_variant;
if(!is_in_face(loc, tm))
return false;
descriptor_variant dv = get_descriptor_from_location(loc, tm);
if(const vertex_descriptor* vd_ptr = boost::get<vertex_descriptor>(&dv))
return (v == *vd_ptr);
return false;
}
/// \ingroup PMP_locate_grp
///
/// \brief Given a `Face_location`, that is an ordered pair composed of a
/// `boost::graph_traits<TriangleMesh>::%face_descriptor` and an array
/// of barycentric coordinates, returns whether the 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 `(w0, w1, w2)`, the correspondance with the weights 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 A model of `FaceGraph`
///
/// \pre `loc.first` is a face descriptor corresponding to a face of `tm`.
/// \pre `tm` is a triangulated surface mesh.
///
template <typename TriangleMesh>
bool
is_on_halfedge(const typename Locate_types<TriangleMesh>::Face_location& loc,
const typename boost::graph_traits<TriangleMesh>::halfedge_descriptor h,
const TriangleMesh& tm)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename Locate_types<TriangleMesh>::descriptor_variant descriptor_variant;
if(!is_in_face(loc, tm))
return false;
descriptor_variant dv = get_descriptor_from_location(loc, tm);
if(const vertex_descriptor* vd_ptr = boost::get<vertex_descriptor>(&dv))
return (*vd_ptr == source(h, tm) || *vd_ptr == target(h, tm));
else if(const halfedge_descriptor* hd_ptr = boost::get<halfedge_descriptor>(&dv))
return (*hd_ptr == h);
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 `(w0, w1, w2)`, the correspondance with the weights 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 A model of `FaceGraph`
///
/// \pre `tm` is a triangulated surface mesh.
///
template <typename TriangleMesh>
bool
is_in_face(const typename Locate_types<TriangleMesh>::Barycentric_coordinates& bar,
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] < 0.)
return false;
}
return true;
}
/// \ingroup PMP_locate_grp
///
/// \brief Given a `Face_location`, that is an ordered pair composed of a
/// `boost::graph_traits<TriangleMesh>::%face_descriptor` and an array
/// of barycentric coordinates, 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 `(w0, w1, w2)`, the correspondance with the weights 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 A model of `FaceGraph`
///
/// \pre `loc.first` is a face descriptor corresponding to a face of `tm`.
/// \pre `tm` is a triangulated surface mesh.
///
template <typename TriangleMesh>
bool
is_in_face(const typename Locate_types<TriangleMesh>::Face_location& loc,
const TriangleMesh& tm)
{
return is_in_face(loc.second, tm);
}
/// \ingroup PMP_locate_grp
///
/// \brief Given a `Face_location`, that is an ordered pair composed of a
/// `boost::graph_traits<TriangleMesh>::%face_descriptor` and an array
/// of barycentric coordinates, 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 `(w0, w1, w2)`, the correspondance with the weights 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 A model of `FaceGraph`
///
/// \pre `loc.first` is a face descriptor corresponding to a face of `tm`.
/// \pre `tm` is a triangulated surface mesh.
///
template <typename TriangleMesh>
bool
is_on_face_border(const typename Locate_types<TriangleMesh>::Face_location& loc,
const TriangleMesh& tm)
{
typedef typename Locate_types<TriangleMesh>::Face_location Face_location;
typedef typename Face_location::second_type Barycentric_coordinates;
if(!is_in_face(loc, tm))
return false;
const Barycentric_coordinates& bar = loc.second;
for(int i=0; i<3; ++i)
if(bar[i] == 0.)
return true;
return false;
}
/// \ingroup PMP_locate_grp
///
/// \brief Given a `Face_location`, that is an ordered pair composed of a
/// `boost::graph_traits<TriangleMesh>::%face_descriptor` and an array
/// of barycentric coordinates, 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 `(w0, w1, w2)`, the correspondance with the weights 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 A model of `FaceGraph`
///
/// \pre `loc.first` is a face descriptor corresponding to a face of `tm`.
/// \pre `tm` is a triangulated surface mesh.
///
template <typename TriangleMesh>
bool
is_on_mesh_border(const typename Locate_types<TriangleMesh>::Face_location& loc,
const TriangleMesh& tm)
{
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef typename Locate_types<TriangleMesh>::Face_location Face_location;
typedef typename Face_location::second_type Barycentric_coordinates;
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] == 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] == 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;
}
/// @}
/// \ingroup PMP_locate_grp
///
/// \brief Given two faces `fd1` and `fd2` of a polygonal mesh `pm`, returns
/// (if it exists) a common edge. The returned halfedge is incident to `fd1`.
///
/// \tparam TriangleMesh A model of `HalfedgeGraph`
///
template<typename PolygonMesh>
boost::optional<typename boost::graph_traits<PolygonMesh>::halfedge_descriptor>
common_halfedge(const typename boost::graph_traits<PolygonMesh>::face_descriptor fd1,
const typename boost::graph_traits<PolygonMesh>::face_descriptor fd2,
const PolygonMesh& pm)
{
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
if(fd1 == fd2)
return halfedge(fd1, pm);
halfedge_descriptor hd = halfedge(fd1, pm), done = hd;
do
{
if(face(opposite(hd, pm), pm) == fd2)
return hd;
hd = next(hd, pm);
}
while(hd != done);
return boost::none;
}
/// \name Point Location
/// @{
/// \ingroup PMP_locate_grp
///
/// \brief Returns the location of the given vertex `vd` as a `Face_location`,
/// that is an ordered pair specifying a face containing the location
/// 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 `(w0, w1, w2)`, the correspondance with the weights 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 A model of `FaceGraph`
///
/// \param vd a vertex of `tm`
/// \param tm a triangulated surface mesh
///
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
locate_in_face(typename boost::graph_traits<TriangleMesh>::vertex_descriptor vd,
const TriangleMesh& tm)
{
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef typename Locate_types<TriangleMesh>::FT FT;
halfedge_descriptor he = halfedge(vd, tm);
// Find a real face in case 'he' is a border halfedge
for(halfedge_descriptor hd : halfedges_around_target(he, tm))
{
if(!is_border(hd, tm))
{
he = hd;
break;
}
}
CGAL_postcondition(!CGAL::is_border(he, tm)); // must find a 'real' face incident to 'vd'
face_descriptor fd = face(he, tm);
CGAL_assertion(target(he, tm) == vd);
CGAL_assertion(fd != boost::graph_traits<TriangleMesh>::null_face());
FT coords[3] = { FT(0), FT(0), FT(0) };
he = next(he, tm); // so that source(he, tm) == vd and it's simpler to handle 'index_in_face'
std::size_t halfedge_local_index = halfedge_index_in_face(he, 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 `Face_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 `(w0, w1, w2)`, the correspondance with the weights 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 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
///
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
locate_in_face(const typename boost::graph_traits<TriangleMesh>::vertex_descriptor vd,
const typename boost::graph_traits<TriangleMesh>::face_descriptor fd,
const TriangleMesh& tm)
{
typedef typename Locate_types<TriangleMesh>::FT FT;
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 `he` and a scalar `t`
/// as `p = (1 - t) * source(he, tm) + t * target(he, tm)`,
/// returns this location along the given edge as a `Face_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 `(w0, w1, w2)`, the correspondance with the weights 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 A model of `FaceGraph`
///
/// \param he a halfedge of `tm`
/// \param t the parametric distance of the desired point along `he`
/// \param tm a triangulation surface mesh
///
/// \pre `tm` is a triangulated surface mesh.
///
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
locate_in_face(const typename boost::graph_traits<TriangleMesh>::halfedge_descriptor he,
const typename Locate_types<TriangleMesh>::FT t,
const TriangleMesh& tm)
{
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef typename Locate_types<TriangleMesh>::FT FT;
face_descriptor fd = face(he, tm);
std::size_t edge_local_index = halfedge_index_in_face(he, 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 triangulation surface mesh,
/// returns this location as a `Face_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 `(w0, w1, w2)`, the correspondance with the weights 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 A model of `FaceGraph`
/// \tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// \param query a point
/// \param fd a face of `tm`
/// \param tm a triangulated surface mesh
/// \param np an optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `tm`.
/// If this parameter is omitted, an internal property map for
/// `boost::vertex_point_t` should be available in `TriangleMesh`.
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits}
/// a geometric traits class instance, model of `Kernel`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
template <typename TriangleMesh, typename NamedParameters>
typename Locate_types<TriangleMesh>::Face_location
locate_in_face(const typename Locate_types<TriangleMesh, NamedParameters>::Point& query,
const typename boost::graph_traits<TriangleMesh>::face_descriptor fd,
const TriangleMesh& tm,
const NamedParameters& np)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename Locate_types<TriangleMesh>::FT FT;
// VertexPointMap
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type Geom_traits;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
typedef typename boost::property_traits<VertexPointMap>::value_type Point;
VertexPointMap vpm = boost::choose_param(boost::get_param(np, internal_np::vertex_point),
get_const_property_map(boost::vertex_point, tm));
Geom_traits gt = choose_param(get_param(np, internal_np::geom_traits), Geom_traits());
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& p0 = get(vpm, vd0);
const Point& p1 = get(vpm, vd1);
const Point& p2 = get(vpm, vd2);
std::array<FT, 3> coords = barycentric_coordinates<Geom_traits, Point>(p0, p1, p2, query, gt);
if(!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<TriangleMesh>(coords); // @tmp keep or not ?
}
return std::make_pair(fd, coords);
}
#ifndef DOXYGEN_RUNNING // because this is in the middle of a @{ @} doxygen group
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
locate_in_face(const typename property_map_value<TriangleMesh, boost::vertex_point_t>::type& query,
const typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& tm)
{
return locate_in_face(query, f, tm, parameters::all_default());
}
#endif
/// \ingroup PMP_locate_grp
///
/// \brief Given a `Face_location`, that is an ordered pair composed of a
/// `boost::graph_traits<TriangleMesh>::%face_descriptor` and an array
/// of barycentric coordinates, and a second face adjacent to the first,
/// return the `Face_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 `(w0, w1, w2)`, the correspondance with the weights 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 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 `he` belongs
///
/// \pre `loc` corresponds to a point that lies on a face incident to both `loc.first` and `fd`.
///
template <typename TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
locate_in_adjacent_face(const typename Locate_types<TriangleMesh>::Face_location& loc,
const typename boost::graph_traits<TriangleMesh>::face_descriptor fd,
const TriangleMesh& tm)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef boost::variant<vertex_descriptor,
halfedge_descriptor,
face_descriptor> descriptor_variant;
typedef typename Locate_types<TriangleMesh>::Face_location Face_location;
typedef typename Locate_types<TriangleMesh>::FT FT;
if(loc.first == fd)
return loc;
Face_location loc_in_fd = std::make_pair(fd, CGAL::make_array(FT(0), FT(0), FT(0)));
descriptor_variant dv = get_descriptor_from_location(loc, tm);
if(const vertex_descriptor* vd_ptr = boost::get<vertex_descriptor>(&dv))
{
int index_of_vd = vertex_index_in_face(*vd_ptr, fd, tm);
loc_in_fd.second[index_of_vd] = 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<halfedge_descriptor>(&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<TriangleMesh>::null_face());
CGAL_assertion(fd != boost::graph_traits<TriangleMesh>::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
{
const face_descriptor fd2 = boost::get<face_descriptor>(dv);
CGAL_assertion(fd2 != boost::graph_traits<TriangleMesh>::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 <typename TriangleMesh>
bool
locate_in_common_face(typename Locate_types<TriangleMesh>::Face_location& known_location,
const typename Locate_types<TriangleMesh>::Point& query,
typename Locate_types<TriangleMesh>::Face_location& query_location,
const TriangleMesh& tm,
const typename Locate_types<TriangleMesh>::FT tolerance =
std::numeric_limits<typename Locate_types<TriangleMesh>::FT>::epsilon())
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef boost::variant<vertex_descriptor, halfedge_descriptor, face_descriptor> descriptor_variant;
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<vertex_descriptor>(&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<TriangleMesh>::null_face())
continue;
// check if 'query' can be found in that face
query_location = locate_in_face(query, fd, tm);
internal::snap_location_to_border<TriangleMesh>(query_location, 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<halfedge_descriptor>(&dv))
{
const halfedge_descriptor hd = *hd_ptr;
face_descriptor fd = face(hd, tm);
if(fd != boost::graph_traits<TriangleMesh>::null_face())
{
query_location = locate_in_face(query, fd, tm);
internal::snap_location_to_border<TriangleMesh>(query_location, 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);
is_query_location_in_face = is_in_face(query_location, tm);
}
}
else
{
const face_descriptor fd = boost::get<face_descriptor>(dv);
CGAL_precondition(fd != boost::graph_traits<TriangleMesh>::null_face());
query_location = locate_in_face(query, fd, tm);
internal::snap_location_to_border<TriangleMesh>(query_location, 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 <typename TriangleMesh>
bool
locate_in_common_face(typename Locate_types<TriangleMesh>::Face_location& first_location,
typename Locate_types<TriangleMesh>::Face_location& second_location,
const TriangleMesh& tm)
{
typedef typename boost::graph_traits<TriangleMesh>::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<face_descriptor> first_incident_faces;
std::set<face_descriptor> 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<face_descriptor>::const_iterator fit = first_incident_faces.begin();
typename std::set<face_descriptor>::const_iterator fend = first_incident_faces.end();
typename std::set<face_descriptor>::const_iterator sit = second_incident_faces.begin();
typename std::set<face_descriptor>::const_iterator send = second_incident_faces.end();
while(fit!=fend && sit!=send)
{
if(*fit == boost::graph_traits<TriangleMesh>::null_face())
++fit;
if(*sit == boost::graph_traits<TriangleMesh>::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<TriangleMesh>::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<typename PolygonMesh,
typename Point,
int dim = CGAL::Ambient_dimension<Point>::value>
struct Point_to_Point_3 // 2D case
{
typedef typename Locate_types<PolygonMesh>::Geom_traits::Point_3 Point_3;
Point_3 operator()(const Point& p) const { return Point_3(p.x(), p.y(), 0); }
};
template<typename PolygonMesh>
struct Point_to_Point_3<PolygonMesh,
typename Locate_types<PolygonMesh>::Geom_traits::Point_3,
3> // 3D case with nothing to do
{
typedef typename Locate_types<PolygonMesh>::Geom_traits::Point_3 Point_3;
const Point_3& operator()(const Point_3& p) const { return p; }
};
template<typename PolygonMesh, typename Point>
struct Point_to_Point_3<PolygonMesh, Point, 3> // Generic 3D case
{
typedef typename Locate_types<PolygonMesh>::Geom_traits::Point_3 Point_3;
Point_3 operator()(const Point& p) const { return Point_3(p.x(), p.y(), p.z()); }
};
template<typename PolygonMesh>
struct Ray_to_Ray_3 // 2D case
{
typedef typename Locate_types<PolygonMesh>::Geom_traits Geom_traits;
typedef typename Geom_traits::Ray_2 Ray_2;
typedef typename Geom_traits::Ray_3 Ray_3;
typedef Point_to_Point_3<PolygonMesh, typename Geom_traits::Point_2> 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<typename PolygonMesh,
typename VertexPointMap = typename property_map_selector<PolygonMesh,
boost::vertex_point_t>::const_type>
struct Point_to_Point_3_VPM
{
private:
typedef VertexPointMap VPM;
typedef Point_to_Point_3_VPM<PolygonMesh, VPM> Self;
public:
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::property_traits<VPM>::value_type Point;
typedef Point_to_Point_3<PolygonMesh, Point> P_to_P3;
typedef typename CGAL::Kernel_traits<Point>::Kernel K;
typedef typename K::Point_3 Point_3;
// required typedefs
typedef typename boost::property_traits<VertexPointMap>::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 PolygonMesh& 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 <typename TriangleMesh, typename AABBTraits, typename VPM>
void build_AABB_tree(const TriangleMesh& tm,
AABB_tree<AABBTraits>& outTree,
const VPM& wrapped_vpm,
typename std::enable_if<
std::is_same<
typename AABBTraits::Point_3, typename boost::property_traits<VPM>::value_type
>::value>::type* = 0)
{
typename boost::graph_traits<TriangleMesh>::face_iterator ffirst, fbeyond;
boost::tie(ffirst, fbeyond) = faces(tm);
outTree.rebuild(ffirst, fbeyond, tm, wrapped_vpm);
outTree.build();
}
template <typename TriangleMesh, typename AABBTraits, typename VPM>
void build_AABB_tree(const TriangleMesh& tm,
AABB_tree<AABBTraits>& outTree,
const VPM& vpm,
typename std::enable_if<
!std::is_same<
typename AABBTraits::Point_3, typename boost::property_traits<VPM>::value_type
>::value>::type* = 0)
{
typedef internal::Point_to_Point_3_VPM<TriangleMesh, 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
/// @{
/// \ingroup PMP_locate_grp
///
/// \brief creates an `AABB_tree` suitable for use with `locate_with_AABB_tree()`.
///
/// \details This function should be called to create and cache an AABB tree
/// to avoid the AABB tree being rebuilt at every call of `locate_with_AABB_tree()`.
///
/// \tparam TriangleMesh A model of `FaceListGraph`
/// \tparam AABBTraits A model of `AABBTraits` used to define a \cgal `AABB_tree`.
/// \tparam NamedParameters a sequence of \ref pmp_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 pmp_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `tm`.
/// If this parameter is omitted, an internal property map for
/// `boost::vertex_point_t` should be available in `TriangleMesh`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
template <typename TriangleMesh, typename AABBTraits, typename NamedParameters>
void build_AABB_tree(const TriangleMesh& tm,
AABB_tree<AABBTraits>& outTree,
const NamedParameters& np)
{
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
const VertexPointMap vpm = boost::choose_param(boost::get_param(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 <typename TriangleMesh, typename AABBTraits>
void build_AABB_tree(const TriangleMesh& tm,
AABB_tree<AABBTraits>& 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 `Face_location`.
///
/// \tparam TriangleMesh A model of `FaceListGraph`
/// \tparam AABBTraits A model of `AABBTraits` used to define a \cgal `AABB_tree`.
/// \tparam NamedParameters a sequence of \ref pmp_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 pmp_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `tm`.
/// If this parameter is omitted, an internal property map for
/// `boost::vertex_point_t` should be available in `TriangleMesh`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
template <typename TriangleMesh, typename AABBTraits, typename NamedParameters>
typename Locate_types<TriangleMesh>::Face_location
locate_with_AABB_tree(const typename Locate_types<TriangleMesh, NamedParameters>::Point& p,
const AABB_tree<AABBTraits>& tree,
const TriangleMesh& tm,
const NamedParameters& np)
{
typedef typename Locate_types<TriangleMesh, NamedParameters>::Point Point;
typedef internal::Point_to_Point_3<TriangleMesh, Point> P_to_P3;
typedef typename AABBTraits::Point_3 Point_3;
CGAL_static_assertion((std::is_same<Point_3, typename P_to_P3::Point_3>::value));
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
typedef internal::Point_to_Point_3_VPM<TriangleMesh, VertexPointMap> WrappedVPM;
const Point_3& p3 = P_to_P3()(p);
typename AABB_tree<AABBTraits>::Point_and_primitive_id result = tree.closest_point_and_primitive(p3);
// The VPM might return a point of any dimension, but the AABB tree necessarily 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 = boost::choose_param(boost::get_param(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 TriangleMesh, typename AABBTraits>
typename Locate_types<TriangleMesh>::Face_location
locate_with_AABB_tree(const typename Locate_types<TriangleMesh>::Point& p,
const AABB_tree<AABBTraits>& 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, 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 AABBTraits must be a model of `AABBTraits` used to define a \cgal `AABB_tree`.
/// \tparam NamedParameters a sequence of \ref pmp_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 pmp_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `tm`.
/// If this parameter is omitted, an internal property map for
/// `boost::vertex_point_t` should be available in `TriangleMesh`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
template <typename TriangleMesh, typename NamedParameters>
typename Locate_types<TriangleMesh>::Face_location
locate(const typename Locate_types<TriangleMesh, NamedParameters>::Point& p,
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<TriangleMesh, NamedParameters>::const_type VertexPointMap;
typedef internal::Point_to_Point_3_VPM<TriangleMesh, VertexPointMap> WrappedVPM;
typedef typename Locate_types<TriangleMesh, NamedParameters>::Point Intrinsic_point;
typedef AABB_face_graph_triangle_primitive<TriangleMesh, WrappedVPM> AABB_face_graph_primitive;
typedef CGAL::AABB_traits<
typename Locate_types<TriangleMesh>::Geom_traits,
AABB_face_graph_primitive> AABB_face_graph_traits;
typedef internal::Point_to_Point_3<TriangleMesh, Intrinsic_point> P_to_P3;
typedef typename AABB_face_graph_traits::Point_3 Point_3;
CGAL_static_assertion((std::is_same<Point_3, typename P_to_P3::Point_3>::value));
const VertexPointMap vpm = boost::choose_param(boost::get_param(np, internal_np::vertex_point),
get_const_property_map(boost::vertex_point, tm));
const WrappedVPM wrapped_vpm(vpm);
AABB_tree<AABB_face_graph_traits> 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 TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
locate(const typename property_map_value<TriangleMesh, boost::vertex_point_t>::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.
///
/// \tparam TriangleMesh must be a model of `FaceListGraph`.
/// \tparam AABBTraits must be a model of `AABBTraits` used to define a \cgal `AABB_tree`.
/// \tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// \param ray Ray to intersect with the input triangulated surface mesh
/// \param tree A `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 pmp_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `tm`.
/// If this parameter is omitted, an internal property map for
/// `boost::vertex_point_t` should be available in `TriangleMesh`.
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits}
/// a geometric traits class instance, model of `Kernel`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
template <typename TriangleMesh, typename AABBTraits, typename NamedParameters>
typename Locate_types<TriangleMesh>::Face_location
locate_with_AABB_tree(const typename Locate_types<TriangleMesh, NamedParameters>::Ray& ray,
const AABB_tree<AABBTraits>& tree,
const TriangleMesh& tm,
const NamedParameters& np)
{
typedef typename Locate_types<TriangleMesh>::Geom_traits 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<TriangleMesh>::face_descriptor face_descriptor;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
typedef internal::Point_to_Point_3_VPM<TriangleMesh, VertexPointMap> WrappedVPM;
typedef internal::Ray_to_Ray_3<TriangleMesh> R_to_R3;
typedef AABB_tree<AABBTraits> AABB_face_graph_tree;
typedef typename AABB_face_graph_tree::template Intersection_and_primitive_id<Ray_3>::Type Intersection_type;
typedef boost::optional<Intersection_type> Ray_intersection;
// First, transform the ray into a 3D ray if needed
Ray_3 ray_3 = R_to_R3()(ray);
std::vector<Ray_intersection> 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<Point_3>(&(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 = boost::choose_param(boost::get_param(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<TriangleMesh>::null_face(),
CGAL::make_array(FT(0), FT(0), FT(0)));
}
#ifndef DOXYGEN_RUNNING
template <typename TriangleMesh, typename AABBTraits>
typename Locate_types<TriangleMesh>::Face_location
locate_with_AABB_tree(const typename Locate_types<TriangleMesh>::Ray& ray,
const AABB_tree<AABBTraits>& 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.
///
/// \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 AABBTraits must be a model of `AABBTraits` used to define a \cgal `AABB_tree`.
/// \tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// \param ray Ray to intersect with the input triangulated surface mesh
/// \param tm the input triangulated surface mesh
/// \param np an optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below:
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `tm`.
/// If this parameter is omitted, an internal property map for
/// `boost::vertex_point_t` should be available in `TriangleMesh`.
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits}
/// a geometric traits class instance, model of `Kernel`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
template <typename TriangleMesh, typename NamedParameters>
typename Locate_types<TriangleMesh>::Face_location
locate(const typename Locate_types<TriangleMesh, NamedParameters>::Ray& ray,
const TriangleMesh& tm,
const NamedParameters& np)
{
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::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<TriangleMesh, VertexPointMap> VPM;
typedef AABB_face_graph_triangle_primitive<TriangleMesh, VPM> AABB_face_graph_primitive;
typedef CGAL::AABB_traits<typename Locate_types<TriangleMesh>::Geom_traits,
AABB_face_graph_primitive> AABB_face_graph_traits;
const VertexPointMap vpm = boost::choose_param(boost::get_param(np, internal_np::vertex_point),
get_const_property_map(boost::vertex_point, tm));
const VPM wrapped_vpm(vpm);
AABB_tree<AABB_face_graph_traits> 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 TriangleMesh>
typename Locate_types<TriangleMesh>::Face_location
locate(const typename internal::Ray_type_selector<
typename Locate_types<TriangleMesh>::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