mirror of https://github.com/CGAL/cgal
Merge pull request #7370 from sloriot/PMP-soup_self_intersections
Add self-intersection test and report functions for triangle soups
This commit is contained in:
commit
ad30839e6b
|
|
@ -110,22 +110,75 @@ public:
|
|||
typedef typename boost::property_traits<PMap>::value_type type;
|
||||
};
|
||||
|
||||
|
||||
template <typename PolygonMesh,
|
||||
typename VPM_from_NP>
|
||||
struct GetVertexPointMap_impl
|
||||
{
|
||||
typedef VPM_from_NP type;
|
||||
typedef VPM_from_NP const_type;
|
||||
|
||||
template<class NamedParameters>
|
||||
static const_type
|
||||
get_const_map(const NamedParameters& np, const PolygonMesh&)
|
||||
{
|
||||
return parameters::get_parameter(np, internal_np::vertex_point);
|
||||
}
|
||||
|
||||
template<class NamedParameters>
|
||||
static type
|
||||
get_map(const NamedParameters& np, PolygonMesh&)
|
||||
{
|
||||
return parameters::get_parameter(np, internal_np::vertex_point);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename PolygonMesh>
|
||||
struct GetVertexPointMap_impl<PolygonMesh, internal_np::Param_not_found>
|
||||
{
|
||||
typedef typename property_map_selector<PolygonMesh, boost::vertex_point_t>::const_type const_type;
|
||||
typedef typename property_map_selector<PolygonMesh, boost::vertex_point_t>::type type;
|
||||
|
||||
template<class NamedParameters>
|
||||
static const_type
|
||||
get_const_map(const NamedParameters& /* np */, const PolygonMesh& pm)
|
||||
{
|
||||
return get_const_property_map(boost::vertex_point, pm);
|
||||
}
|
||||
|
||||
template<class NamedParameters>
|
||||
static type
|
||||
get_map(const NamedParameters& /* np */, PolygonMesh& pm)
|
||||
{
|
||||
return get_property_map(boost::vertex_point, pm);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename PolygonMesh,
|
||||
typename NamedParameters = parameters::Default_named_parameters>
|
||||
class GetVertexPointMap
|
||||
{
|
||||
typedef typename property_map_selector<PolygonMesh, boost::vertex_point_t>::const_type
|
||||
DefaultVPMap_const;
|
||||
typedef typename property_map_selector<PolygonMesh, boost::vertex_point_t>::type
|
||||
DefaultVPMap;
|
||||
typedef typename internal_np::Lookup_named_param_def<internal_np::vertex_point_t,
|
||||
NamedParameters,
|
||||
internal_np::Param_not_found>::type VPM_from_NP;
|
||||
|
||||
typedef GetVertexPointMap_impl<PolygonMesh, VPM_from_NP> Impl;
|
||||
|
||||
public:
|
||||
typedef typename internal_np::Lookup_named_param_def<internal_np::vertex_point_t,
|
||||
NamedParameters,
|
||||
DefaultVPMap>::type type;
|
||||
typedef typename internal_np::Lookup_named_param_def<internal_np::vertex_point_t,
|
||||
NamedParameters,
|
||||
DefaultVPMap_const>::type const_type;
|
||||
typedef typename Impl::type type;
|
||||
typedef typename Impl::const_type const_type;
|
||||
|
||||
static const_type
|
||||
get_const_map(const NamedParameters& np, const PolygonMesh& pm)
|
||||
{
|
||||
return Impl::get_const_map(np, pm);
|
||||
}
|
||||
|
||||
static type
|
||||
get_map(const NamedParameters& np, PolygonMesh& pm)
|
||||
{
|
||||
return Impl::get_map(np, pm);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename PolygonMesh, typename NamedParameters>
|
||||
|
|
@ -138,10 +191,15 @@ public:
|
|||
typedef typename CGAL::Kernel_traits<Point>::Kernel Kernel;
|
||||
};
|
||||
|
||||
template <typename PolygonMesh,
|
||||
typename NamedParametersGT = parameters::Default_named_parameters,
|
||||
typename NamedParametersVPM = NamedParametersGT>
|
||||
class GetGeomTraits
|
||||
|
||||
template<typename PolygonMesh, class GT, class NamedParametersVPM>
|
||||
struct GetGeomTraits_impl
|
||||
{
|
||||
typedef GT type;
|
||||
};
|
||||
|
||||
template<typename PolygonMesh, class NamedParametersVPM>
|
||||
struct GetGeomTraits_impl<PolygonMesh, internal_np::Param_not_found, NamedParametersVPM>
|
||||
{
|
||||
typedef typename CGAL::graph_has_property<PolygonMesh, boost::vertex_point_t>::type Has_internal_pmap;
|
||||
|
||||
|
|
@ -154,12 +212,20 @@ class GetGeomTraits
|
|||
typedef typename boost::mpl::if_c<Has_internal_pmap::value ||
|
||||
!std::is_same<internal_np::Param_not_found, NP_vpm>::value,
|
||||
typename GetK<PolygonMesh, NamedParametersVPM>::Kernel,
|
||||
Fake_GT>::type DefaultKernel;
|
||||
Fake_GT>::type type;
|
||||
};
|
||||
|
||||
public:
|
||||
template <typename PolygonMesh,
|
||||
typename NamedParametersGT = parameters::Default_named_parameters,
|
||||
typename NamedParametersVPM = NamedParametersGT>
|
||||
struct GetGeomTraits
|
||||
{
|
||||
typedef typename internal_np::Lookup_named_param_def<internal_np::geom_traits_t,
|
||||
NamedParametersGT,
|
||||
DefaultKernel>::type type;
|
||||
internal_np::Param_not_found>::type GT_from_NP;
|
||||
typedef typename GetGeomTraits_impl<PolygonMesh,
|
||||
GT_from_NP,
|
||||
NamedParametersVPM>::type type;
|
||||
};
|
||||
|
||||
// Define the following structs:
|
||||
|
|
@ -278,6 +344,21 @@ public:
|
|||
typedef typename CGAL::Identity_property_map<const Dummy_point> const_type;
|
||||
};
|
||||
|
||||
template <typename PointRange, typename NamedParameters>
|
||||
struct GetPolygonSoupGeomTraits
|
||||
{
|
||||
typedef typename internal_np::Lookup_named_param_def <
|
||||
internal_np::geom_traits_t,
|
||||
NamedParameters,
|
||||
typename CGAL::Kernel_traits<
|
||||
typename boost::property_traits<
|
||||
typename GetPointMap<PointRange, NamedParameters>::type
|
||||
>::value_type
|
||||
>::type
|
||||
> ::type type;
|
||||
};
|
||||
|
||||
|
||||
template <class PointRange, class NamedParameters, class PointMap = Default, class NormalMap = Default>
|
||||
struct Point_set_processing_3_np_helper
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ CGAL tetrahedral Delaunay refinement algorithm.
|
|||
|
||||
- Added a named parameter to `CGAL::Polygon_mesh_processing::smooth_shape()` to disable scaling to compensate volume loss.
|
||||
|
||||
- Added the functions `CGAL::Polygon_mesh_processing::does_triangle_soup_self_intersect()` and `CGAL::Polygon_mesh_processing::triangle_soup_self_intersections()` to identify and report self-intersections in a triangle soup, similarly to existing functions on triangle meshes.
|
||||
|
||||
### [3D Simplicial Mesh Data Structure](https://doc.cgal.org/5.6/Manual/packages.html#PkgSMDS3) (new package)
|
||||
|
||||
- This new package wraps all the existing code that deals with a `MeshComplex_3InTriangulation_3` to describe 3D simplicial meshes, and makes the data structure independent from the tetrahedral mesh generation package.
|
||||
|
|
|
|||
|
|
@ -152,6 +152,8 @@ The page \ref bgl_namedparameters "Named Parameters" describes their usage.
|
|||
\cgalCRPSection{Intersection Functions}
|
||||
- `CGAL::Polygon_mesh_processing::does_self_intersect()`
|
||||
- `CGAL::Polygon_mesh_processing::self_intersections()`
|
||||
- `CGAL::Polygon_mesh_processing::does_triangle_soup_self_intersect()`
|
||||
- `CGAL::Polygon_mesh_processing::triangle_soup_self_intersections()`
|
||||
- \link PMP_intersection_grp `CGAL::Polygon_mesh_processing::do_intersect()` \endlink
|
||||
- `CGAL::Polygon_mesh_processing::intersecting_meshes()`
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#endif
|
||||
|
||||
#include <boost/iterator/function_output_iterator.hpp>
|
||||
#include <boost/range/irange.hpp>
|
||||
|
||||
#include <exception>
|
||||
#include <sstream>
|
||||
|
|
@ -54,6 +55,127 @@ namespace CGAL {
|
|||
namespace Polygon_mesh_processing {
|
||||
namespace internal {
|
||||
|
||||
template <class TM>
|
||||
struct Triangle_mesh_and_triangle_soup_wrapper
|
||||
{
|
||||
typedef typename boost::graph_traits<TM>::face_descriptor face_descriptor;
|
||||
typedef typename boost::graph_traits<TM>::vertex_descriptor vertex_descriptor;
|
||||
typedef typename boost::graph_traits<TM>::halfedge_descriptor halfedge_descriptor; // private
|
||||
|
||||
static void get_face_vertices(face_descriptor fd, std::array<vertex_descriptor,3>& vh, const TM& tm)
|
||||
{
|
||||
CGAL_assertion(boost::graph_traits<TM>::null_face() != fd);
|
||||
halfedge_descriptor h = halfedge(fd, tm);
|
||||
vh[0]=source(h, tm);
|
||||
vh[1]=target(h, tm);
|
||||
vh[2]=target(next(h, tm), tm);
|
||||
}
|
||||
|
||||
static bool faces_have_a_shared_edge(face_descriptor f, face_descriptor g, std::array<vertex_descriptor, 4>& vh, const TM& tm)
|
||||
{
|
||||
CGAL_assertion(boost::graph_traits<TM>::null_face() != f);
|
||||
CGAL_assertion(boost::graph_traits<TM>::null_face() != g);
|
||||
halfedge_descriptor h=halfedge(f, tm);
|
||||
for(unsigned int i=0; i<3; ++i)
|
||||
{
|
||||
halfedge_descriptor opp_h = opposite(h, tm);
|
||||
if(face(opp_h, tm) == g)
|
||||
{
|
||||
vh[0]=source(h, tm);
|
||||
vh[1]=target(h, tm);
|
||||
vh[2]=target(next(h, tm), tm);
|
||||
vh[3]=target(next(opp_h, tm), tm);
|
||||
return true;
|
||||
}
|
||||
h = next(h, tm);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_pure_triangle(const TM& tm)
|
||||
{
|
||||
return is_triangle_mesh(tm);
|
||||
}
|
||||
};
|
||||
|
||||
template <class PointRange, class TriangleRange>
|
||||
struct Triangle_mesh_and_triangle_soup_wrapper<
|
||||
std::pair<const PointRange&,
|
||||
const TriangleRange&>>
|
||||
{
|
||||
typedef std::size_t face_descriptor;
|
||||
typedef std::size_t vertex_descriptor;
|
||||
|
||||
typedef std::pair<const PointRange&, const TriangleRange& > Soup;
|
||||
|
||||
static void get_face_vertices(face_descriptor fd, std::array<vertex_descriptor,3>& vh, const Soup& soup)
|
||||
{
|
||||
const auto& face = soup.second[fd];
|
||||
vh[0]=face[0];
|
||||
vh[1]=face[1];
|
||||
vh[2]=face[2];
|
||||
}
|
||||
|
||||
static bool faces_have_a_shared_edge(face_descriptor fd, face_descriptor gd, std::array<vertex_descriptor, 4>& vh, const Soup& soup)
|
||||
{
|
||||
const auto& f = soup.second[fd];
|
||||
const auto& g = soup.second[gd];
|
||||
|
||||
for(unsigned int i=0; i<2; ++i) // no need to check f[2] if neither f[0] nor f[1] are shared
|
||||
{
|
||||
for(unsigned int j=0; j<3; ++j)
|
||||
{
|
||||
if (f[i]==g[j])
|
||||
{
|
||||
vh[0]=f[i];
|
||||
vh[1]=f[i+1];
|
||||
vh[2]=f[(i+2)%3];
|
||||
|
||||
if (vh[1]==g[(j+1)%3])
|
||||
{
|
||||
vh[3]=g[(j+2)%3];
|
||||
return true;
|
||||
}
|
||||
if (vh[1]==g[(j+2)%3])
|
||||
{
|
||||
vh[3]=g[(j+1)%3];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (i==0)
|
||||
{
|
||||
vh[1]=f[i];
|
||||
vh[2]=f[(i+1)%3];
|
||||
vh[0]=f[(i+2)%3];
|
||||
if (vh[0]==g[(j+1)%3])
|
||||
{
|
||||
vh[3]=g[(j+2)%3];
|
||||
return true;
|
||||
}
|
||||
if (vh[0]==g[(j+2)%3])
|
||||
{
|
||||
vh[3]=g[(j+1)%3];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_pure_triangle(const Soup& soup)
|
||||
{
|
||||
for (const typename std::iterator_traits<typename TriangleRange::const_iterator>::value_type& t : soup.second)
|
||||
if (t.size()!=3)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Output_iterator>
|
||||
struct Throw_at_count_reached_functor {
|
||||
|
||||
|
|
@ -82,59 +204,49 @@ struct Throw_at_count_reached_functor {
|
|||
|
||||
// Checks for 'real' intersections, i.e. not simply a shared vertex or edge
|
||||
template <class GT, class TM, class VPM>
|
||||
bool do_faces_intersect(typename boost::graph_traits<TM>::halfedge_descriptor h,
|
||||
typename boost::graph_traits<TM>::halfedge_descriptor g,
|
||||
bool do_faces_intersect(typename Triangle_mesh_and_triangle_soup_wrapper<TM>::face_descriptor fh,
|
||||
typename Triangle_mesh_and_triangle_soup_wrapper<TM>::face_descriptor fg,
|
||||
const TM& tmesh,
|
||||
const VPM vpmap,
|
||||
const typename GT::Construct_segment_3& construct_segment,
|
||||
const typename GT::Construct_triangle_3& construct_triangle,
|
||||
const typename GT::Do_intersect_3& do_intersect)
|
||||
{
|
||||
typedef typename boost::graph_traits<TM>::vertex_descriptor vertex_descriptor;
|
||||
typedef typename boost::graph_traits<TM>::halfedge_descriptor halfedge_descriptor;
|
||||
typedef Triangle_mesh_and_triangle_soup_wrapper<TM> Wrapper;
|
||||
typedef typename Wrapper::vertex_descriptor vertex_descriptor;
|
||||
|
||||
typedef typename GT::Segment_3 Segment;
|
||||
typedef typename GT::Triangle_3 Triangle;
|
||||
typedef typename GT::Segment_3 Segment;
|
||||
typedef typename GT::Triangle_3 Triangle;
|
||||
|
||||
CGAL_assertion(!is_border(h, tmesh));
|
||||
CGAL_assertion(!is_border(g, tmesh));
|
||||
|
||||
vertex_descriptor hv[3], gv[3];
|
||||
hv[0] = target(h, tmesh);
|
||||
hv[1] = target(next(h, tmesh), tmesh);
|
||||
hv[2] = source(h, tmesh);
|
||||
|
||||
gv[0] = target(g, tmesh);
|
||||
gv[1] = target(next(g, tmesh), tmesh);
|
||||
gv[2] = source(g, tmesh);
|
||||
std::array<vertex_descriptor, 3> hv, gv;
|
||||
Wrapper::get_face_vertices(fh, hv, tmesh);
|
||||
Wrapper::get_face_vertices(fg, gv, tmesh);
|
||||
|
||||
// check for shared edge
|
||||
for(unsigned int i=0; i<3; ++i)
|
||||
std::array<vertex_descriptor, 4> verts;
|
||||
if (Wrapper::faces_have_a_shared_edge(fh, fg, verts, tmesh))
|
||||
{
|
||||
halfedge_descriptor opp_h = opposite(h, tmesh);
|
||||
if(face(opp_h, tmesh) == face(g, tmesh))
|
||||
{
|
||||
// there is an intersection if the four points are coplanar and the triangles overlap
|
||||
if(CGAL::coplanar(get(vpmap, hv[i]),
|
||||
get(vpmap, hv[(i+1)%3]),
|
||||
get(vpmap, hv[(i+2)%3]),
|
||||
get(vpmap, target(next(opp_h, tmesh), tmesh))) &&
|
||||
CGAL::coplanar_orientation(get(vpmap, hv[(i+2)%3]),
|
||||
get(vpmap, hv[i]),
|
||||
get(vpmap, hv[(i+1)%3]),
|
||||
get(vpmap, target(next(opp_h, tmesh), tmesh)))
|
||||
== CGAL::POSITIVE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// there is a shared edge but no intersection
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (verts[2]==verts[3]) return false; // only for a soup of triangles
|
||||
|
||||
h = next(h, tmesh);
|
||||
// there is an intersection if the four points are coplanar and the triangles overlap
|
||||
if(CGAL::coplanar(get(vpmap, verts[0]),
|
||||
get(vpmap, verts[1]),
|
||||
get(vpmap, verts[2]),
|
||||
get(vpmap, verts[3])) &&
|
||||
CGAL::coplanar_orientation(get(vpmap, verts[0]),
|
||||
get(vpmap, verts[1]),
|
||||
get(vpmap, verts[2]),
|
||||
get(vpmap, verts[3]))
|
||||
== CGAL::POSITIVE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// there is a shared edge but no intersection
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check for shared vertex --> maybe intersection, maybe not
|
||||
|
|
@ -188,8 +300,6 @@ template <class Box, class TM, class VPM, class GT,
|
|||
class OutputIterator>
|
||||
struct Strict_intersect_faces // "strict" as in "not sharing a subface"
|
||||
{
|
||||
typedef typename boost::graph_traits<TM>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
mutable OutputIterator m_iterator;
|
||||
const TM& m_tmesh;
|
||||
const VPM m_vpmap;
|
||||
|
|
@ -209,10 +319,7 @@ struct Strict_intersect_faces // "strict" as in "not sharing a subface"
|
|||
|
||||
void operator()(const Box* b, const Box* c) const
|
||||
{
|
||||
const halfedge_descriptor h = halfedge(b->info(), m_tmesh);
|
||||
const halfedge_descriptor g = halfedge(c->info(), m_tmesh);
|
||||
|
||||
if(do_faces_intersect<GT>(h, g, m_tmesh, m_vpmap, m_construct_segment, m_construct_triangle, m_do_intersect))
|
||||
if(do_faces_intersect<GT>(b->info(), c->info(), m_tmesh, m_vpmap, m_construct_segment, m_construct_triangle, m_do_intersect))
|
||||
*m_iterator++ = std::make_pair(b->info(), c->info());
|
||||
}
|
||||
};
|
||||
|
|
@ -229,15 +336,17 @@ self_intersections_impl(const FaceRange& face_range,
|
|||
const bool throw_on_SI,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
CGAL_precondition(CGAL::is_triangle_mesh(tmesh));
|
||||
typedef TriangleMesh TM;
|
||||
typedef Triangle_mesh_and_triangle_soup_wrapper<TM> Wrapper;
|
||||
|
||||
CGAL_precondition(Wrapper::is_pure_triangle(tmesh));
|
||||
|
||||
using CGAL::parameters::choose_parameter;
|
||||
using CGAL::parameters::get_parameter;
|
||||
using CGAL::parameters::is_default_parameter;
|
||||
|
||||
typedef TriangleMesh TM;
|
||||
typedef typename boost::graph_traits<TM>::halfedge_descriptor halfedge_descriptor;
|
||||
typedef typename boost::graph_traits<TM>::face_descriptor face_descriptor;
|
||||
typedef typename Wrapper::face_descriptor face_descriptor;
|
||||
typedef typename Wrapper::vertex_descriptor vertex_descriptor;
|
||||
|
||||
typedef CGAL::Box_intersection_d::ID_FROM_BOX_ADDRESS Box_policy;
|
||||
typedef CGAL::Box_intersection_d::Box_with_info_d<double, 3, face_descriptor, Box_policy> Box;
|
||||
|
|
@ -245,9 +354,9 @@ self_intersections_impl(const FaceRange& face_range,
|
|||
typedef typename GetGeomTraits<TM, NamedParameters>::type GT;
|
||||
GT gt = choose_parameter<GT>(get_parameter(np, internal_np::geom_traits));
|
||||
|
||||
typedef typename GetVertexPointMap<TM, NamedParameters>::const_type VPM;
|
||||
VPM vpmap = choose_parameter(get_parameter(np, internal_np::vertex_point),
|
||||
get_const_property_map(boost::vertex_point, tmesh));
|
||||
typedef GetVertexPointMap<TM, NamedParameters> VPM_helper;
|
||||
typedef typename VPM_helper::const_type VPM;
|
||||
VPM vpmap = VPM_helper::get_const_map(np, tmesh);
|
||||
|
||||
const bool do_limit = !(is_default_parameter<NamedParameters, internal_np::maximum_number_t>::value);
|
||||
const unsigned int maximum_number = choose_parameter(get_parameter(np, internal_np::maximum_number), 0);
|
||||
|
|
@ -268,11 +377,13 @@ self_intersections_impl(const FaceRange& face_range,
|
|||
// This loop is very cheap, so there is hardly anything to gain from parallelizing it
|
||||
for(face_descriptor f : face_range)
|
||||
{
|
||||
halfedge_descriptor h = halfedge(f, tmesh);
|
||||
std::array<vertex_descriptor, 3> vh;
|
||||
Wrapper::get_face_vertices(f, vh, tmesh);
|
||||
|
||||
typename boost::property_traits<VPM>::reference
|
||||
p = get(vpmap, target(h,tmesh)),
|
||||
q = get(vpmap, target(next(h, tmesh), tmesh)),
|
||||
r = get(vpmap, target(prev(h, tmesh), tmesh));
|
||||
p = get(vpmap, vh[0]),
|
||||
q = get(vpmap, vh[1]),
|
||||
r = get(vpmap, vh[2]);
|
||||
|
||||
// tiny fixme: if f is degenerate, we might still have a real intersection between f
|
||||
// and another face f', but right now we are not creating a box for f and thus not returning those
|
||||
|
|
@ -342,7 +453,7 @@ self_intersections_impl(const FaceRange& face_range,
|
|||
std::atomic<unsigned int> atomic_counter(counter);
|
||||
Throw_functor throwing_count_functor(atomic_counter, maximum_number, std::back_inserter(face_pairs));
|
||||
Throwing_after_count_output_iterator count_filter(throwing_count_functor);
|
||||
Filtered_intersecting_faces_filter limited_callback(tmesh, vpmap, gt, count_filter);
|
||||
Filtered_intersecting_faces_filter limited_callback(tmesh, vpmap, gt, count_filter);
|
||||
CGAL::box_self_intersection_d<ConcurrencyTag>(box_ptr.begin(), box_ptr.end(), limited_callback, cutoff);
|
||||
}
|
||||
catch(const CGAL::internal::Throw_at_output_exception&)
|
||||
|
|
@ -408,9 +519,9 @@ self_intersections_impl(const FaceRange& face_range,
|
|||
*
|
||||
* collects intersections between a subset of faces of a triangulated surface mesh.
|
||||
* Two faces are said to intersect if the corresponding triangles intersect
|
||||
* and the intersection is not an edge nor a vertex incident to both faces.
|
||||
* and the intersection is neither an edge nor a vertex incident to both faces.
|
||||
*
|
||||
* This function depends on the package \ref PkgBoxIntersectionD
|
||||
* This function depends on the package \ref PkgBoxIntersectionD.
|
||||
*
|
||||
* @pre `CGAL::is_triangle_mesh(tmesh)`
|
||||
*
|
||||
|
|
@ -475,9 +586,9 @@ self_intersections(const FaceRange& face_range,
|
|||
*
|
||||
* collects intersections between all the faces of a triangulated surface mesh.
|
||||
* Two faces are said to intersect if the corresponding triangles intersect
|
||||
* and the intersection is not an edge nor a vertex incident to both faces.
|
||||
* and the intersection is neither an edge nor a vertex incident to both faces.
|
||||
*
|
||||
* This function depends on the package \ref PkgBoxIntersectionD
|
||||
* This function depends on the package \ref PkgBoxIntersectionD.
|
||||
*
|
||||
* @pre `CGAL::is_triangle_mesh(tmesh)`
|
||||
*
|
||||
|
|
@ -490,9 +601,9 @@ self_intersections(const FaceRange& face_range,
|
|||
*
|
||||
* @param tmesh the triangulated surface mesh to be checked
|
||||
* @param out output iterator to be filled with all pairs of non-adjacent faces that intersect.
|
||||
In case `tmesh` contains some degenerate faces, for each degenerate face `f` a pair `(f,f)`
|
||||
will be put in `out` before any other self intersection between non-degenerate faces.
|
||||
These are the only pairs where degenerate faces will be reported.
|
||||
* In case `tmesh` contains some degenerate faces, for each degenerate face `f` a pair `(f,f)`
|
||||
* will be put in `out` before any other self intersection between non-degenerate faces. <br>
|
||||
* Note that these are the only pairs where degenerate faces will be reported.
|
||||
* @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
|
|
@ -541,7 +652,7 @@ self_intersections(const TriangleMesh& tmesh,
|
|||
*
|
||||
* \brief tests if a set of faces of a triangulated surface mesh self-intersects.
|
||||
*
|
||||
* This function depends on the package \ref PkgBoxIntersectionD
|
||||
* This function depends on the package \ref PkgBoxIntersectionD.
|
||||
*
|
||||
* @pre `CGAL::is_triangle_mesh(tmesh)`
|
||||
*
|
||||
|
|
@ -584,8 +695,6 @@ bool does_self_intersect(const FaceRange& face_range,
|
|||
const TriangleMesh& tmesh,
|
||||
const NamedParameters& np = parameters::default_values())
|
||||
{
|
||||
CGAL_precondition(CGAL::is_triangle_mesh(tmesh));
|
||||
|
||||
try
|
||||
{
|
||||
CGAL::Emptyset_iterator unused_out;
|
||||
|
|
@ -614,7 +723,7 @@ bool does_self_intersect(const FaceRange& face_range,
|
|||
*
|
||||
* \brief tests if a triangulated surface mesh self-intersects.
|
||||
*
|
||||
* This function depends on the package \ref PkgBoxIntersectionD
|
||||
* This function depends on the package \ref PkgBoxIntersectionD.
|
||||
*
|
||||
* @pre `CGAL::is_triangle_mesh(tmesh)`
|
||||
*
|
||||
|
|
@ -656,6 +765,219 @@ bool does_self_intersect(const TriangleMesh& tmesh,
|
|||
return does_self_intersect<ConcurrencyTag>(faces(tmesh), tmesh, np);
|
||||
}
|
||||
|
||||
|
||||
#ifndef DOXYGEN_RUNNING
|
||||
template <class PointRange, class VPM>
|
||||
struct Property_map_for_soup
|
||||
{
|
||||
typedef std::size_t key_type;
|
||||
typedef typename boost::property_traits<VPM>::value_type value_type;
|
||||
//typedef typename boost::property_traits<VPM>::category category;
|
||||
typedef boost::readable_property_map_tag category;
|
||||
typedef typename boost::property_traits<VPM>::reference reference;
|
||||
|
||||
const PointRange& points;
|
||||
VPM vpm;
|
||||
|
||||
Property_map_for_soup(const PointRange& points, VPM vpm)
|
||||
: points(points)
|
||||
, vpm(vpm)
|
||||
{}
|
||||
|
||||
inline friend
|
||||
reference get(const Property_map_for_soup<PointRange, VPM>& map, key_type k)
|
||||
{
|
||||
return get(map.vpm, map.points[k]);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \ingroup PMP_intersection_grp
|
||||
*
|
||||
* collects intersections between all the triangles in a triangle soup.
|
||||
*
|
||||
* Two triangles of the soup are said to intersect if the corresponding geometric triangles intersect
|
||||
* and the intersection is neither an edge nor a vertex of both triangles
|
||||
* (with the same point ids, ignoring the orientation for an edge).
|
||||
*
|
||||
* This function depends on the package \ref PkgBoxIntersectionD.
|
||||
*
|
||||
* @tparam ConcurrencyTag enables sequential versus parallel algorithm.
|
||||
* Possible values are `Sequential_tag`, `Parallel_tag`, and `Parallel_if_available_tag`.
|
||||
* @tparam PointRange a model of the concept `RandomAccessContainer`
|
||||
* whose value type is the point type
|
||||
* @tparam TriangleRange a model of the concept `RandomAccessContainer` whose
|
||||
* value type is a model of the concept `RandomAccessContainer` whose value type is `std::size_t`
|
||||
* @tparam TriangleIdPairOutputIterator a model of `OutputIterator` holding objects of type
|
||||
* `std::pair<std::size_t,std::size_t>`
|
||||
* @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||
*
|
||||
* @param points points of the soup of triangles
|
||||
* @param triangles each element in the range describes a triangle using the indices of the points in `points`
|
||||
* @param out output iterator to be filled with all pairs of ids of triangles intersecting (the id of a triangle is its position in `triangles`).
|
||||
* In case the triangle soup contains some degenerate triangles, for each degenerate triangle `t` with id `i` a pair `(i,i)`
|
||||
* will be put in `out` before any other self intersection between non-degenerate triangles.<br>
|
||||
* Note that these are the only pairs where degenerate triangles will be reported.
|
||||
* @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamNBegin{maximum_number}
|
||||
* \cgalParamDescription{the maximum number of self intersections that will be detected and returned by the function.}
|
||||
* \cgalParamType{unsigned int}
|
||||
* \cgalParamDefault{No limit.}
|
||||
* \cgalParamExtra{In parallel mode, the number of returned self-intersections is at least `maximum_number`
|
||||
* (and not exactly that number) as no strong synchronization is put on threads for performance reasons.}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{point_map}
|
||||
* \cgalParamDescription{a property map associating points to the elements of the range `points`}
|
||||
* \cgalParamType{a model of `ReadablePropertyMap` whose value type is a point type from a \cgal `Kernel`.}
|
||||
* \cgalParamDefault{`CGAL::Identity_property_map`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{geom_traits}
|
||||
* \cgalParamDescription{an instance of a geometric traits class}
|
||||
* \cgalParamType{a class model of `PMPSelfIntersectionTraits`}
|
||||
* \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`}
|
||||
* \cgalParamExtra{The geometric traits class must be compatible with the point type of the point map.}
|
||||
* \cgalParamNEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* @return `out`
|
||||
*
|
||||
* @sa `does_triangle_soup_self_intersect()`
|
||||
* @sa `self_intersections()`
|
||||
* @sa `does_self_intersect()`
|
||||
*/
|
||||
template <class ConcurrencyTag = Sequential_tag,
|
||||
class PointRange,
|
||||
class TriangleRange,
|
||||
class TriangleIdPairOutputIterator,
|
||||
class CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
TriangleIdPairOutputIterator
|
||||
triangle_soup_self_intersections(const PointRange& points,
|
||||
const TriangleRange& triangles,
|
||||
TriangleIdPairOutputIterator out,
|
||||
const CGAL_NP_CLASS& np = parameters::default_values())
|
||||
{
|
||||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
using parameters::is_default_parameter;
|
||||
|
||||
typedef typename CGAL::GetPointMap<PointRange, CGAL_NP_CLASS>::const_type Point_map_base;
|
||||
Point_map_base pm_base = choose_parameter<Point_map_base>(get_parameter(np, internal_np::point_map));
|
||||
typedef Property_map_for_soup<PointRange, Point_map_base> Point_map;
|
||||
typedef typename GetPolygonSoupGeomTraits<PointRange, CGAL_NP_CLASS>::type GT;
|
||||
GT gt = choose_parameter<GT>(get_parameter(np, internal_np::geom_traits));
|
||||
|
||||
const bool do_limit = !(is_default_parameter<CGAL_NP_CLASS, internal_np::maximum_number_t>::value);
|
||||
if (do_limit)
|
||||
{
|
||||
return self_intersections<ConcurrencyTag>(boost::irange<std::size_t>(0, triangles.size()),
|
||||
std::make_pair(std::cref(points), std::cref(triangles)),
|
||||
out,
|
||||
parameters::vertex_point_map(Point_map(points,pm_base)).
|
||||
geom_traits(gt).
|
||||
maximum_number(choose_parameter(get_parameter(np, internal_np::maximum_number), 0)));
|
||||
}
|
||||
|
||||
return self_intersections<ConcurrencyTag>(boost::irange<std::size_t>(0, triangles.size()),
|
||||
std::make_pair(std::cref(points), std::cref(triangles)),
|
||||
out,
|
||||
parameters::vertex_point_map(Point_map(points,pm_base)).
|
||||
geom_traits(gt));
|
||||
}
|
||||
|
||||
/**
|
||||
* \ingroup PMP_intersection_grp
|
||||
*
|
||||
* \brief tests if a triangle soup self-intersects.
|
||||
*
|
||||
* A triangle soup self-intersects if at least two triangles of the soup intersect.
|
||||
* Two triangles of the soup are said to intersect if the corresponding geometric triangles intersect
|
||||
* and the intersection is neither an edge nor a vertex of both triangles
|
||||
* (with the same point ids, ignoring the orientation for an edge).
|
||||
*
|
||||
* This function depends on the package \ref PkgBoxIntersectionD.
|
||||
*
|
||||
* @tparam ConcurrencyTag enables sequential versus parallel algorithm.
|
||||
* Possible values are `Sequential_tag`, `Parallel_tag`, and `Parallel_if_available_tag`.
|
||||
* @tparam PointRange a model of the concept `RandomAccessContainer`
|
||||
* whose value type is the point type
|
||||
* @tparam TriangleRange a model of the concept `RandomAccessContainer` whose
|
||||
* value type is a model of the concept `RandomAccessContainer` whose value type is `std::size_t`
|
||||
* @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
|
||||
*
|
||||
* @param points points of the soup of triangles
|
||||
* @param triangles each element in the range describes a triangle using the indices of the points in `points`
|
||||
* @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
|
||||
*
|
||||
* \cgalNamedParamsBegin
|
||||
* \cgalParamNBegin{point_map}
|
||||
* \cgalParamDescription{a property map associating points to the elements of the range `points`}
|
||||
* \cgalParamType{a model of `ReadablePropertyMap` whose value type is a point type from a \cgal `Kernel`.}
|
||||
* \cgalParamDefault{`CGAL::Identity_property_map`}
|
||||
* \cgalParamNEnd
|
||||
*
|
||||
* \cgalParamNBegin{geom_traits}
|
||||
* \cgalParamDescription{an instance of a geometric traits class}
|
||||
* \cgalParamType{a class model of `PMPSelfIntersectionTraits`}
|
||||
* \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`}
|
||||
* \cgalParamExtra{The geometric traits class must be compatible with the point type of the point map.}
|
||||
* \cgalParamNEnd
|
||||
* \cgalNamedParamsEnd
|
||||
*
|
||||
* @return `true` if the triangle soup self-intersects, and `false` otherwise.
|
||||
*
|
||||
* @sa `triangle_soup_self_intersections()`
|
||||
* @sa `self_intersections()`
|
||||
* @sa `does_self_intersect()`
|
||||
*/
|
||||
template <class ConcurrencyTag = Sequential_tag,
|
||||
class PointRange,
|
||||
class TriangleRange,
|
||||
class CGAL_NP_TEMPLATE_PARAMETERS>
|
||||
bool does_triangle_soup_self_intersect(const PointRange& points,
|
||||
const TriangleRange& triangles,
|
||||
const CGAL_NP_CLASS& np = parameters::default_values())
|
||||
{
|
||||
try
|
||||
{
|
||||
using parameters::choose_parameter;
|
||||
using parameters::get_parameter;
|
||||
|
||||
CGAL::Emptyset_iterator unused_out;
|
||||
typedef typename CGAL::GetPointMap<PointRange, CGAL_NP_CLASS>::const_type Point_map_base;
|
||||
Point_map_base pm_base = choose_parameter<Point_map_base>(get_parameter(np, internal_np::point_map));
|
||||
typedef Property_map_for_soup<PointRange, Point_map_base> Point_map;
|
||||
typedef typename GetPolygonSoupGeomTraits<PointRange, CGAL_NP_CLASS>::type GT;
|
||||
GT gt = choose_parameter<GT>(get_parameter(np, internal_np::geom_traits));
|
||||
|
||||
internal::self_intersections_impl<ConcurrencyTag>(boost::irange<std::size_t>(0, triangles.size()),
|
||||
std::make_pair(std::cref(points), std::cref(triangles)),
|
||||
unused_out, true /*throw*/,
|
||||
parameters::vertex_point_map(Point_map(points,pm_base))
|
||||
.geom_traits(gt));
|
||||
}
|
||||
catch (const CGAL::internal::Throw_at_output_exception&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#if defined(CGAL_LINKED_WITH_TBB) && TBB_USE_CAPTURED_EXCEPTION
|
||||
catch (const tbb::captured_exception& e)
|
||||
{
|
||||
const char* ti1 = e.name();
|
||||
const char* ti2 = typeid(const CGAL::internal::Throw_at_output_exception&).name();
|
||||
const std::string tn1(ti1);
|
||||
const std::string tn2(ti2);
|
||||
if (tn1 == tn2) return true;
|
||||
else throw;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
}// namespace Polygon_mesh_processing
|
||||
}// namespace CGAL
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ create_single_source_cgal_program("point_inside_surface_mesh_test.cpp")
|
|||
create_single_source_cgal_program("polygon_mesh_slicer_test.cpp")
|
||||
create_single_source_cgal_program("self_intersection_polyhedron_test.cpp")
|
||||
create_single_source_cgal_program("self_intersection_surface_mesh_test.cpp")
|
||||
create_single_source_cgal_program("self_intersection_triangle_soup_test.cpp")
|
||||
create_single_source_cgal_program("pmp_do_intersect_test.cpp")
|
||||
create_single_source_cgal_program("test_is_polygon_soup_a_polygon_mesh.cpp")
|
||||
create_single_source_cgal_program("test_stitching.cpp")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,234 @@
|
|||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
|
||||
#include <CGAL/IO/polygon_soup_io.h>
|
||||
#include <CGAL/tags.h>
|
||||
#include <CGAL/Timer.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel EPICK;
|
||||
typedef CGAL::Exact_predicates_exact_constructions_kernel EPECK;
|
||||
|
||||
namespace PMP = ::CGAL::Polygon_mesh_processing;
|
||||
namespace CP = ::CGAL::parameters;
|
||||
|
||||
template <typename K>
|
||||
int test_self_intersections(const std::string filename,
|
||||
const bool expected)
|
||||
{
|
||||
std::vector<typename K::Point_3> points;
|
||||
std::vector< std::array<std::size_t, 3> > triangles;
|
||||
|
||||
bool read_ok = CGAL::IO::read_polygon_soup(filename, points, triangles);
|
||||
|
||||
if ( !read_ok ) {
|
||||
std::cerr << "Error: cannot read file: " << filename << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Reading file: " << filename << std::endl;
|
||||
|
||||
CGAL::Timer timer;
|
||||
timer.start();
|
||||
|
||||
std::vector<std::pair<std::size_t, std::size_t> > intersected_tris;
|
||||
|
||||
PMP::triangle_soup_self_intersections<CGAL::Parallel_if_available_tag>(points, triangles, std::back_inserter(intersected_tris));
|
||||
bool intersecting_1 = !intersected_tris.empty();
|
||||
|
||||
std::cout << "self_intersections test took " << timer.time() << " sec." << std::endl;
|
||||
std::cout << intersected_tris.size() << " pairs of triangles are intersecting." << std::endl;
|
||||
|
||||
timer.reset();
|
||||
|
||||
bool intersecting_2 =
|
||||
PMP::does_triangle_soup_self_intersect<CGAL::Parallel_if_available_tag>(points, triangles);
|
||||
|
||||
std::cout << "does_self_intersect test took " << timer.time() << " sec." << std::endl;
|
||||
std::cout << (intersecting_2 ? "There is a self-intersection." :
|
||||
"There are no self-intersections.") << std::endl;
|
||||
|
||||
assert(intersecting_1 == intersecting_2);
|
||||
assert(intersecting_1 == expected);
|
||||
|
||||
std::cout << filename << " passed the tests." << std::endl << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
int test_limited_self_intersections(const std::string& filename)
|
||||
{
|
||||
std::vector<typename K::Point_3> points;
|
||||
std::vector< std::array<std::size_t, 3> > triangles;
|
||||
|
||||
bool read_ok = CGAL::IO::read_polygon_soup(filename, points, triangles);
|
||||
|
||||
if ( !read_ok ) {
|
||||
std::cerr << "Error: cannot read file: " << filename << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
CGAL::Timer timer;
|
||||
timer.start();
|
||||
|
||||
std::vector<std::pair<std::size_t, std::size_t> > intersected_tris;
|
||||
|
||||
#ifdef CGAL_LINKED_WITH_TBB
|
||||
PMP::triangle_soup_self_intersections()<CGAL::Parallel_if_available_tag>(
|
||||
points, triangles,
|
||||
std::back_inserter(intersected_tris), CGAL::parameters::maximum_number(40));
|
||||
std::cout << "self_intersections test for 40 SI took " << timer.time() << " sec." << std::endl;
|
||||
std::cout << "Found " << intersected_tris.size() << " SIs." << std::endl;
|
||||
if(intersected_tris.size() < 40)
|
||||
{
|
||||
std::cerr<<"Not enough intersections found in parallel."<<std::endl;
|
||||
return 1;
|
||||
}
|
||||
intersected_tris.clear();
|
||||
timer.reset();
|
||||
#endif
|
||||
PMP::triangle_soup_self_intersections<CGAL::Sequential_tag>(
|
||||
points, triangles,
|
||||
std::back_inserter(intersected_tris), CGAL::parameters::maximum_number(40));
|
||||
std::cout << "self_intersections test for 40 SI took " << timer.time() << " sec." << std::endl;
|
||||
timer.reset();
|
||||
if(intersected_tris.size() != 40)
|
||||
{
|
||||
std::cerr<<"Too many intersections found in sequential"<<std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// If file(s) are provided, the associated expected result must also be provided.
|
||||
// Note that this expected value is a Boolean that is passed in command line
|
||||
// with either 'true' or 'false' (and not integers), that is for example:
|
||||
// > self_intersection_surface_mesh_test data/U.off false
|
||||
|
||||
// First test ----------------------------------------------------------------
|
||||
bool expected = false;
|
||||
std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/elephant.off");
|
||||
if(argc > 1) {
|
||||
assert(argc > 2);
|
||||
std::stringstream ss(argv[2]);
|
||||
ss >> std::boolalpha >> expected;
|
||||
assert(!ss.fail()); // make sure that argv[2] is either 'true' or 'false'
|
||||
}
|
||||
|
||||
std::cout << "First test (EPICK):" << std::endl;
|
||||
int r = test_self_intersections<EPICK>(filename, expected);
|
||||
|
||||
std::cout << "First test (EPECK):" << std::endl;
|
||||
r += test_self_intersections<EPECK>(filename, expected);
|
||||
|
||||
// Second test ---------------------------------------------------------------
|
||||
expected = true;
|
||||
filename = (argc > 3) ? argv[3] : CGAL::data_file_path("meshes/mannequin-devil.off");
|
||||
if(argc > 3) {
|
||||
assert(argc > 4);
|
||||
std::stringstream ss(argv[4]);
|
||||
ss >> std::boolalpha >> expected;
|
||||
assert(!ss.fail());
|
||||
}
|
||||
|
||||
std::cout << "Second test (EPICK):" << std::endl;
|
||||
r += test_self_intersections<EPICK>(filename, expected);
|
||||
|
||||
std::cout << "Second test (EPECK):" << std::endl;
|
||||
r += test_self_intersections<EPECK>(filename, expected);
|
||||
|
||||
// Third test ----------------------------------------------------------------
|
||||
expected = true;
|
||||
filename = (argc > 5) ? argv[5] : "data/overlapping_triangles.off";
|
||||
if(argc > 5) {
|
||||
assert(argc > 6);
|
||||
std::stringstream ss(argv[6]);
|
||||
ss >> std::boolalpha >> expected;
|
||||
assert(!ss.fail());
|
||||
}
|
||||
|
||||
std::cout << "Third test (EPICK):" << std::endl;
|
||||
r += test_self_intersections<EPICK>(filename, expected);
|
||||
|
||||
std::cout << "Third test (EPECK):" << std::endl;
|
||||
r += test_self_intersections<EPECK>(filename, expected);
|
||||
|
||||
// Fourth test ----------------------------------------------------------------
|
||||
expected = true;
|
||||
filename = (argc > 7) ? argv[7] : "data_degeneracies/degtri_single.off";
|
||||
if(argc > 7) {
|
||||
assert(argc > 8);
|
||||
std::stringstream ss(argv[8]);
|
||||
ss >> std::boolalpha >> expected;
|
||||
assert(!ss.fail());
|
||||
}
|
||||
|
||||
std::cout << "Fourth test (EPICK):" << std::endl;
|
||||
r += test_self_intersections<EPICK>(filename, expected);
|
||||
|
||||
std::cout << "Fourth test (EPECK):" << std::endl;
|
||||
r += test_self_intersections<EPECK>(filename, expected);
|
||||
|
||||
filename = (argc > 9) ? argv[9] : CGAL::data_file_path("meshes/mannequin-devil.off");
|
||||
|
||||
std::cout << "Test with maximum_number (EPICK):" << std::endl;
|
||||
r += test_limited_self_intersections<EPICK>(filename);
|
||||
|
||||
std::cout << "Test with maximum_number (EPECK):" << std::endl;
|
||||
r += test_limited_self_intersections<EPECK>(filename);
|
||||
|
||||
// extra hand written tests
|
||||
{
|
||||
// shared edge with same point: no self-intersection
|
||||
typedef EPICK::Point_3 Point_3;
|
||||
std::vector<EPICK::Point_3> points = {Point_3(0,0,0), Point_3(1,0,0), Point_3(0,1,0), Point_3(0,-1,0)};
|
||||
std::vector< std::array<std::size_t, 3> > triangles={{0,1,2}, {0,1,3}};
|
||||
assert(!PMP::does_triangle_soup_self_intersect(points, triangles));
|
||||
}
|
||||
{
|
||||
// shared edge with duplicated points: self-intersection
|
||||
typedef EPICK::Point_3 Point_3;
|
||||
std::vector<EPICK::Point_3> points = {Point_3(0,0,0), Point_3(1,0,0), Point_3(0,1,0),Point_3(0,0,0), Point_3(1,0,0), Point_3(0,-1,0)};
|
||||
std::vector< std::array<std::size_t, 3> > triangles={{0,1,2}, {3,4,5}};
|
||||
assert(PMP::does_triangle_soup_self_intersect(points, triangles));
|
||||
}
|
||||
{
|
||||
// shared vertex with same point: no self-intersection
|
||||
typedef EPICK::Point_3 Point_3;
|
||||
std::vector<EPICK::Point_3> points = {Point_3(0,0,0), Point_3(1,0,0), Point_3(0,1,0), Point_3(0,2,0), Point_3(1,2,0)};
|
||||
std::vector< std::array<std::size_t, 3> > triangles={{0,1,2}, {3,4,2}};
|
||||
assert(!PMP::does_triangle_soup_self_intersect(points, triangles));
|
||||
}
|
||||
{
|
||||
// shared vertex with duplicated points: self-intersection
|
||||
typedef EPICK::Point_3 Point_3;
|
||||
std::vector<EPICK::Point_3> points = {Point_3(0,0,0), Point_3(1,0,0), Point_3(0,1,0), Point_3(0,2,0), Point_3(1,2,0), Point_3(0,1,0)};
|
||||
std::vector< std::array<std::size_t, 3> > triangles={{0,1,2}, {3,4,5}};
|
||||
assert(PMP::does_triangle_soup_self_intersect(points, triangles));
|
||||
}
|
||||
{
|
||||
// 4 triangles around a shared edge: no self-intersection
|
||||
typedef EPICK::Point_3 Point_3;
|
||||
std::vector<EPICK::Point_3> points = {Point_3(0,0,0), Point_3(1,0,0), Point_3(0,1,0), Point_3(0,-1,0), Point_3(0,0,1), Point_3(0,0,-1)};
|
||||
std::vector< std::array<std::size_t, 3> > triangles={{0,1,2},{0,1,3},{0,1,4},{0,1,5}};
|
||||
assert(!PMP::does_triangle_soup_self_intersect(points, triangles));
|
||||
}
|
||||
{
|
||||
// 4 triangles around a shared edge but two triangles intersecting: self-intersection
|
||||
typedef EPICK::Point_3 Point_3;
|
||||
std::vector<EPICK::Point_3> points = {Point_3(0,0,0), Point_3(1,0,0), Point_3(0,1,0), Point_3(0,0.5,0), Point_3(0,0,1), Point_3(0,0,-1)};
|
||||
std::vector< std::array<std::size_t, 3> > triangles={{0,1,2},{0,1,3},{0,1,4},{0,1,5}};
|
||||
assert(PMP::does_triangle_soup_self_intersect(points, triangles));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
Loading…
Reference in New Issue