Merge pull request #6826 from MaelRL/T23-Operator=-GF

Improvements for Triangulation_23 comparison operators
This commit is contained in:
Laurent Rineau 2022-09-13 12:32:58 +02:00
commit e037898fc1
4 changed files with 325 additions and 58 deletions

View File

@ -895,49 +895,77 @@ is_valid(bool verbose, int level) const
if (dimension() <= 0 || (dimension()==1 && number_of_vertices() == 2 ) )
return result;
if (dimension() == 1) {
if (dimension() == 1)
{
Finite_vertices_iterator it1 = finite_vertices_begin(),
it2(it1), it3(it1);
++it2;
++it3; ++it3;
while( it3 != finite_vertices_end()) {
Orientation s = orientation(it1->point(),
it2->point(),
it3->point());
result = result && s == COLLINEAR ;
Orientation s = orientation(point(it1), point(it2), point(it3));
result = result && (s == COLLINEAR) ;
if(verbose && (s != COLLINEAR))
{
std::cerr << "Error: " << point(it1) << " "
<< point(it2) << " and "
<< point(it3) << " are not collinear" << std::endl;
}
CGAL_triangulation_assertion(result);
++it1 ; ++it2; ++it3;
}
}
else { //dimension() == 2
for(Finite_faces_iterator it=finite_faces_begin();
it!=finite_faces_end(); it++) {
else //dimension() == 2
{
for(Finite_faces_iterator it=finite_faces_begin(); it!=finite_faces_end(); it++)
{
CGAL_triangulation_assertion( ! is_infinite(it));
Orientation s = orientation(it->vertex(0)->point(),
it->vertex(1)->point(),
it->vertex(2)->point());
CGAL_triangulation_assertion( s == LEFT_TURN );
Orientation s = orientation(point(it, 0), point(it, 1), point(it, 2));
result = result && ( s == LEFT_TURN );
if(verbose && (s != LEFT_TURN))
{
std::cerr << "Error: " << point(it, 0) << " "
<< point(it, 1) << " and "
<< point(it, 2) << " form a badly oriented face" << std::endl;
}
CGAL_triangulation_assertion(result);
}
Vertex_circulator start = incident_vertices(infinite_vertex());
Vertex_circulator pc(start);
Vertex_circulator qc(start); ++qc;
Vertex_circulator rc(start); ++rc; ++rc;
do {
Orientation s = orientation(pc->point(),
qc->point(),
rc->point());
do
{
Orientation s = orientation(point(pc), point(qc), point(rc));
CGAL_triangulation_assertion( s != LEFT_TURN );
result = result && ( s != LEFT_TURN );
if(verbose && (s == LEFT_TURN))
{
std::cerr << "Error: " << point(pc) << " "
<< point(qc) << " and "
<< point(rc) << " form a badly oriented infinite face" << std::endl;
}
++pc ; ++qc ; ++rc;
} while(pc != start);
// check number of faces. This cannot be done by the Tds
// which does not know the number of components nor the genus
result = result && (number_of_faces() == 2*(number_of_vertices()+1)
- 4
- degree(infinite_vertex()));
const bool genus_check = number_of_faces() == 2*(number_of_vertices()+1) - 4 - degree(infinite_vertex());
result = result && genus_check;
if(verbose && !genus_check)
{
std::cerr << "Error: Genus check fail " << number_of_faces()
<< " vs " << 2*(number_of_vertices()+1) - 4 - degree(infinite_vertex())
<< " (nv = " << number_of_vertices()
<< " nf = " << number_of_faces()
<< " and degree(infinite_vertex()) = " << degree(infinite_vertex()) << std::endl;
}
CGAL_triangulation_assertion( result);
}
return result;
@ -3789,6 +3817,219 @@ operator>>(std::istream& is, Triangulation_2<Gt, Tds> &tr)
return is;
}
namespace internal {
// Internal function used by operator==.
template < class GT, class TDS1, class TDS2, typename FMAP, typename VMAP >
bool
test_next(const Triangulation_2<GT, TDS1>& t1,
const Triangulation_2<GT, TDS2>& t2,
typename Triangulation_2<GT, TDS1>::Face_handle f1,
typename Triangulation_2<GT, TDS2>::Face_handle f2,
FMAP& Fmap,
VMAP& Vmap)
{
// This function tests and registers the 3 neighbors of f1/f2,
// and recursively calls itself over them.
// We don't use the call stack as it may overflow
// Returns false if an inequality has been found.
// Precondition: f1, f2 have been registered as well as their 3 vertices.
CGAL_triangulation_precondition(t1.dimension() >= 2);
CGAL_triangulation_precondition(Fmap[f1] == f2);
CGAL_triangulation_precondition(Vmap.find(f1->vertex(0)) != Vmap.end());
CGAL_triangulation_precondition(Vmap.find(f1->vertex(1)) != Vmap.end());
CGAL_triangulation_precondition(t1.dimension() == 1 || Vmap.find(f1->vertex(2)) != Vmap.end());
typedef Triangulation_2<GT, TDS1> Tr1;
typedef Triangulation_2<GT, TDS2> Tr2;
typedef typename Tr1::Vertex_handle Vertex_handle1;
typedef typename Tr1::Face_handle Face_handle1;
typedef typename Tr2::Vertex_handle Vertex_handle2;
typedef typename Tr2::Face_handle Face_handle2;
typedef typename VMAP::const_iterator Vit;
typedef typename FMAP::const_iterator Fit;
typedef typename Tr1::Geom_traits::Construct_point_2 Construct_point_2;
typedef typename Tr1::Geom_traits::Compare_xy_2 Compare_xy_2;
Compare_xy_2 cmp1 = t1.geom_traits().compare_xy_2_object();
Construct_point_2 cp = t1.geom_traits().construct_point_2_object();
std::vector<std::pair<Face_handle1, Face_handle2> > face_stack;
face_stack.emplace_back(f1, f2);
while(! face_stack.empty())
{
Face_handle1 f1 = face_stack.back().first;
Face_handle2 f2 = face_stack.back().second;
face_stack.pop_back();
for(int i=0; i <= t1.dimension(); ++i)
{
Face_handle1 n1 = f1->neighbor(i);
Fit fit = Fmap.find(n1);
Vertex_handle1 v1 = f1->vertex(i);
Vertex_handle2 v2 = Vmap[v1];
Face_handle2 n2 = f2->neighbor(f2->index(v2));
if(fit != Fmap.end())
{
// n1 was already registered.
if(fit->second != n2)
return false;
continue;
}
// n1 has not yet been registered.
// We check that the new vertices match geometrically.
// And we register them.
Vertex_handle1 vn1 = n1->vertex(n1->index(f1));
Vertex_handle2 vn2 = n2->vertex(n2->index(f2));
Vit vit = Vmap.find(vn1);
if(vit != Vmap.end())
{
// vn1 already registered
if(vit->second != vn2)
return false;
}
else
{
if(t2.is_infinite(vn2))
return false; // vn1 can't be infinite,
// since it would have been registered.
if(cmp1(cp(vn1->point()), cp(vn2->point())) != 0)
return false;
// We register vn1/vn2.
Vmap.emplace(vn1, vn2);
}
// We register n1/n2.
Fmap.emplace(n1, n2);
face_stack.emplace_back(n1, n2);
}
}
return true;
}
} // namespace internal
template < class GT, class TDS1, class TDS2 >
bool
operator==(const Triangulation_2<GT, TDS1>& t1,
const Triangulation_2<GT, TDS2>& t2)
{
typedef typename Triangulation_2<GT, TDS1>::Vertex_handle Vertex_handle1;
typedef typename Triangulation_2<GT, TDS1>::Face_handle Face_handle1;
typedef typename Triangulation_2<GT, TDS2>::Vertex_handle Vertex_handle2;
typedef typename Triangulation_2<GT, TDS2>::Face_handle Face_handle2;
typedef typename Triangulation_2<GT, TDS1>::Point Point;
typedef typename Triangulation_2<GT, TDS1>::Geom_traits::Equal_2 Equal_2;
typedef typename Triangulation_2<GT, TDS1>::Geom_traits::Compare_xy_2 Compare_xy_2;
typedef typename Triangulation_2<GT, TDS1>::Geom_traits::Construct_point_2 Construct_point_2;
Equal_2 equal = t1.geom_traits().equal_2_object();
Compare_xy_2 cmp1 = t1.geom_traits().compare_xy_2_object();
Compare_xy_2 cmp2 = t2.geom_traits().compare_xy_2_object();
Construct_point_2 cp = t1.geom_traits().construct_point_2_object();
// Some quick checks.
if(t1.dimension() != t2.dimension() ||
t1.number_of_vertices() != t2.number_of_vertices() ||
t1.number_of_faces() != t2.number_of_faces())
return false;
int dim = t1.dimension();
// Special case for dimension < 1.
// The triangulation is uniquely defined in these cases.
if(dim == -1)
return true;
// Special case for dimensions 0 and 1.
if(dim < 2)
{
// It's enough to test that the points are the same,
// since the triangulation is uniquely defined in this case.
std::vector<Point> V1 (t1.points_begin(), t1.points_end());
std::vector<Point> V2 (t2.points_begin(), t2.points_end());
std::sort(V1.begin(), V1.end(),
[&cmp1, &cp](const Point& p1, const Point& p2){ return cmp1(cp(p1), cp(p2))==SMALLER; });
std::sort(V2.begin(), V2.end(),
[&cmp2, &cp](const Point& p1, const Point& p2){ return cmp2(cp(p1), cp(p2))==SMALLER; });
return V1 == V2;
}
// We will store the mapping between the 2 triangulations vertices and faces in 2 maps.
std::unordered_map<Vertex_handle1, Vertex_handle2> Vmap;
std::unordered_map<Face_handle1, Face_handle2> Fmap;
// Handle the infinite vertex.
Vertex_handle1 v1 = t1.infinite_vertex();
Vertex_handle2 iv2 = t2.infinite_vertex();
Vmap.emplace(v1, iv2);
// We pick one infinite face of t1, and try to match it against the infinite faces of t2.
Face_handle1 f = v1->face();
Vertex_handle1 v2 = f->vertex((f->index(v1)+1)%(dim+1));
Vertex_handle1 v3 = f->vertex((f->index(v1)+2)%(dim+1));
const Point& p2 = v2->point();
const Point& p3 = v3->point();
std::vector<Face_handle2> ifs;
auto fc = t2.incident_faces(iv2), done(fc);
do {
ifs.push_back(fc);
} while(++fc != done);
for(typename std::vector<Face_handle2>::const_iterator fit = ifs.begin();
fit != ifs.end(); ++fit)
{
int inf = (*fit)->index(iv2);
if(equal(cp(p2), cp((*fit)->vertex((inf+1)%(dim+1))->point())))
Vmap.emplace(v2, (*fit)->vertex((inf+1)%(dim+1)));
else if(dim == 2 && equal(cp(p2), cp((*fit)->vertex((inf+2)%(dim+1))->point())))
Vmap.emplace(v2, (*fit)->vertex((inf+2)%(dim+1)));
else
continue; // None matched v2.
if(equal(cp(p3), cp((*fit)->vertex((inf+1)%(dim+1))->point())))
Vmap.emplace(v3, (*fit)->vertex((inf+1)%(dim+1)));
else if(dim == 2 && equal(cp(p3), cp((*fit)->vertex((inf+2)%(dim+1))->point())))
Vmap.emplace(v3, (*fit)->vertex((inf+2)%(dim+1)));
else
continue; // None matched v3.
// Found it !
Fmap.emplace(f, *fit);
break;
}
if(Fmap.size() == 0)
return false;
// We now have one face, we need to propagate recursively.
return internal::test_next(t1, t2, Fmap.begin()->first, Fmap.begin()->second, Fmap, Vmap);
}
template < class GT, class Tds1, class Tds2 >
inline
bool
operator!=(const Triangulation_2<GT, Tds1>& t1,
const Triangulation_2<GT, Tds2>& t2)
{
return ! (t1 == t2);
}
} //namespace CGAL
#include <CGAL/enable_warnings.h>

View File

@ -132,12 +132,10 @@ _test_cls_triangulation_2( const Triangul & )
assert( T1.number_of_vertices() == 0 );
Triangul T3(T1);
assert(T3.tds().vertices().size() == T1.tds().vertices().size());
assert(T3.tds().faces().size() == T1.tds().faces().size());
assert(T3 == T1);
Triangul T4 = T1;
assert(T4.tds().vertices().size() == T1.tds().vertices().size());
assert(T4.tds().faces().size() == T1.tds().faces().size());
assert(T4 == T1);
T3.swap(T1);
@ -159,6 +157,7 @@ _test_cls_triangulation_2( const Triangul & )
assert( T0_0.number_of_vertices() == 0 );
assert( T0_0.number_of_faces() == 0);
assert( T0_0.is_valid() );
assert( T0_0 == T0_0 );
Triangul T0_1;
Vertex_handle v0_1_0 = T0_1.insert(p0); assert( v0_1_0 != nullptr );
@ -166,10 +165,18 @@ _test_cls_triangulation_2( const Triangul & )
assert( T0_1.number_of_vertices() == 1 );
assert( T0_1.number_of_faces() == 0);
assert( T0_1.is_valid() );
assert( T0_0 != T0_1 );
Triangul T0_1b(T0_1);
assert(T0_1b.tds().vertices().size() == T0_1.tds().vertices().size());
assert(T0_1b.tds().faces().size() == T0_1.tds().faces().size());
assert(T0_1b == T0_1);
Triangul T0_1c;
v0_1_0 = T0_1c.insert(p1); assert( v0_1_0 != nullptr );
assert( T0_1c.dimension() == 0 );
assert( T0_1c.number_of_vertices() == 1 );
assert( T0_1c.number_of_faces() == 0);
assert( T0_1c.is_valid() );
assert( T0_1 != T0_1c );
// test insert_first()
Triangul T0_2;
@ -192,10 +199,10 @@ _test_cls_triangulation_2( const Triangul & )
assert( T1_2.number_of_vertices() == 2 );
assert( T1_2.number_of_faces() == 0 );
assert( T1_2.is_valid() );
assert( T1_2 != T0_1 );
Triangul T1_2b(T1_2);
assert(T1_2b.tds().vertices().size() == T1_2.tds().vertices().size());
assert(T1_2b.tds().faces().size() == T1_2.tds().faces().size());
assert(T1_2b == T1_2);
// p1,p3,p2 [endpoints first]
Triangul T1_3_0;
@ -206,6 +213,7 @@ _test_cls_triangulation_2( const Triangul & )
assert( T1_3_0.number_of_vertices() == 3 );
assert( T1_3_0.number_of_faces() == 0 );
assert( T1_3_0.is_valid() );
assert( T1_3_0 != T1_2 );
// p1,p2,p3 [middle point first]
Triangul T1_3_1;
@ -216,6 +224,7 @@ _test_cls_triangulation_2( const Triangul & )
assert( T1_3_1.number_of_vertices() == 3 );
assert( T1_3_1.number_of_faces() == 0 );
assert( T1_3_1.is_valid() );
assert( T1_3_1 == T1_3_0 );
Triangul T1_5;
Vertex_handle v1_5_1 = T1_5.insert(p1);
@ -227,6 +236,7 @@ _test_cls_triangulation_2( const Triangul & )
assert( T1_5.number_of_vertices() == 5 );
assert( T1_5.number_of_faces() == 0 );
assert( T1_5.is_valid() );
assert( T1_5 != T1_3_1 );
// test insert_second()
Triangul T1_6 = T0_2;
@ -298,11 +308,13 @@ _test_cls_triangulation_2( const Triangul & )
// test generic iterator insert
#ifndef CGAL_CFG_NO_MEMBER_TEMPLATES
Triangul T2_4; T2_4.insert( Point_iterator(T2_1.finite_vertices_begin()),
Point_iterator(T2_1.finite_vertices_end()) );
Triangul T2_4;
T2_4.insert( Point_iterator(T2_1.finite_vertices_begin()),
Point_iterator(T2_1.finite_vertices_end()) );
assert( T2_4.dimension() == 2 );
assert( T2_4.number_of_vertices() == 11 );
assert( T2_4.is_valid() );
assert( T2_4 == T2_1 );
#endif
// test list iterator insert
@ -320,6 +332,7 @@ _test_cls_triangulation_2( const Triangul & )
assert( T2_6.dimension() == 2 );
assert( T2_6.number_of_vertices() == 10 );
assert( T2_6.is_valid() );
assert( T2_5 == T2_6 );
// test grid insert
Triangul T2_7;
@ -496,21 +509,25 @@ _test_cls_triangulation_2( const Triangul & )
assert( T1_5_2.dimension() == 1 );
assert( T1_5_2.number_of_vertices() == 5 );
assert( T1_5_2.is_valid() );
assert( T1_5_2 == T1_5 );
// test copy_constructor with non-empty 2-triangulation
Triangul T2_8_1(T2_8);
assert( T2_8_1.is_valid());
assert( T2_8 == T2_8);
Triangul T2_1_1( T2_1 );
assert( T2_1_1.dimension() == 2 );
assert( T2_1_1.number_of_vertices() == 11 );
assert( T2_1_1.is_valid() );
assert( T2_1_1 == T2_1 );
// test assignment operator
Triangul T2_1_4 = T2_1;
assert( T2_1_4.dimension() == 2 );
assert( T2_1_4.number_of_vertices() == 11 );
assert( T2_1_4.is_valid() );
assert( T2_1_4 == T2_1 );
/*********************************************/
/****** FINITE/INFINITE VERTICES/FACES *******/

View File

@ -24,14 +24,6 @@
# include <CGAL/Profile_counter.h>
#endif
#include <iostream>
#include <list>
#include <set>
#include <map>
#include <unordered_map>
#include <utility>
#include <stack>
#include <CGAL/Unique_hash_map.h>
#include <CGAL/triangulation_assertions.h>
#include <CGAL/Triangulation_utils_3.h>
@ -77,6 +69,14 @@
# include <tbb/scalable_allocator.h>
#endif
#include <iostream>
#include <list>
#include <set>
#include <map>
#include <unordered_map>
#include <utility>
#include <stack>
#define CGAL_TRIANGULATION_3_USE_THE_4_POINTS_CONSTRUCTOR
namespace CGAL {
@ -7154,16 +7154,14 @@ is_valid_finite(Cell_handle c, bool verbose, int) const
namespace internal {
// Internal function used by operator==.
template < class GT, class Tds1, class Tds2, class Lds >
template < class GT, class Tds1, class Tds2, class Lds, typename CMAP, typename VMAP >
bool
test_next(const Triangulation_3<GT, Tds1, Lds>& t1,
const Triangulation_3<GT, Tds2, Lds>& t2,
typename Triangulation_3<GT, Tds1, Lds>::Cell_handle c1,
typename Triangulation_3<GT, Tds2, Lds>::Cell_handle c2,
std::map<typename Triangulation_3<GT, Tds1, Lds>::Cell_handle,
typename Triangulation_3<GT, Tds2, Lds>::Cell_handle>& Cmap,
std::map<typename Triangulation_3<GT, Tds1, Lds>::Vertex_handle,
typename Triangulation_3<GT, Tds2, Lds>::Vertex_handle>& Vmap)
CMAP& Cmap,
VMAP& Vmap)
{
// This function tests and registers the 4 neighbors of c1/c2,
// and recursively calls itself over them.
@ -7186,8 +7184,8 @@ test_next(const Triangulation_3<GT, Tds1, Lds>& t1,
typedef typename Tr2::Vertex_handle Vertex_handle2;
typedef typename Tr2::Cell_handle Cell_handle2;
typedef typename std::map<Vertex_handle1, Vertex_handle2>::const_iterator Vit;
typedef typename std::map<Cell_handle1, Cell_handle2>::const_iterator Cit;
typedef typename std::unordered_map<Vertex_handle1, Vertex_handle2>::const_iterator Vit;
typedef typename std::unordered_map<Cell_handle1, Cell_handle2>::const_iterator Cit;
typedef typename Tr1::Geom_traits::Construct_point_3 Construct_point_3;
typedef typename Tr1::Geom_traits::Compare_xyz_3 Compare_xyz_3;
@ -7196,7 +7194,7 @@ test_next(const Triangulation_3<GT, Tds1, Lds>& t1,
Construct_point_3 cp = t1.geom_traits().construct_point_3_object();
std::vector<std::pair<Cell_handle1, Cell_handle2> > cell_stack;
cell_stack.push_back(std::make_pair(c1, c2));
cell_stack.emplace_back(c1, c2);
while(! cell_stack.empty())
{
@ -7242,12 +7240,12 @@ test_next(const Triangulation_3<GT, Tds1, Lds>& t1,
return false;
// We register vn1/vn2.
Vmap.insert(std::make_pair(vn1, vn2));
Vmap.emplace(vn1, vn2);
}
// We register n1/n2.
Cmap.insert(std::make_pair(n1, n2));
cell_stack.push_back(std::make_pair(n1, n2));
Cmap.emplace(n1, n2);
cell_stack.emplace_back(n1, n2);
}
}
@ -7286,11 +7284,11 @@ operator==(const Triangulation_3<GT, Tds1, Lds>& t1,
int dim = t1.dimension();
// Special case for dimension < 1.
// The triangulation is uniquely defined in these cases.
if(dim < 1)
if(dim == - 1)
return true;
// Special case for dimension == 1.
if(dim == 1)
// Special case for dimensions 0 and 1.
if(dim < 2)
{
// It's enough to test that the points are the same,
// since the triangulation is uniquely defined in this case.
@ -7308,13 +7306,13 @@ operator==(const Triangulation_3<GT, Tds1, Lds>& t1,
// We will store the mapping between the 2 triangulations vertices and
// cells in 2 maps.
std::map<Vertex_handle1, Vertex_handle2> Vmap;
std::map<Cell_handle1, Cell_handle2> Cmap;
std::unordered_map<Vertex_handle1, Vertex_handle2> Vmap;
std::unordered_map<Cell_handle1, Cell_handle2> Cmap;
// Handle the infinite vertex.
Vertex_handle1 v1 = t1.infinite_vertex();
Vertex_handle2 iv2 = t2.infinite_vertex();
Vmap.insert(std::make_pair(v1, iv2));
Vmap.emplace(v1, iv2);
// We pick one infinite cell of t1, and try to match it against the
// infinite cells of t2.
@ -7364,7 +7362,7 @@ operator==(const Triangulation_3<GT, Tds1, Lds>& t1,
}
// Found it !
Cmap.insert(std::make_pair(c, *cit));
Cmap.emplace(c, *cit);
break;
}

View File

@ -222,15 +222,23 @@ _test_cls_triangulation_3(const Triangulation &)
}
std::cout << " Constructor1 " << std::endl;
Point p10(0,0,0);
Vertex_handle v0=T0.insert(p10);
Vertex_handle v0=T0.insert(p[0]);
assert(T0.dimension() == 0);
assert(T0.number_of_vertices() == 1);
assert(T0.is_valid());
assert(T0 != Tm1);
Cls T0d0(T0);
assert(T0 == T0d0);
Cls T0d0b;
v0=T0d0b.insert(p[1]);
assert(T0d0b.dimension() == 0);
assert(T0d0b.number_of_vertices() == 1);
assert(T0d0b.is_valid());
assert(T0d0b != T0d0);
assert(T0d0b != Tm1);
if (! del) // to avoid doing the following tests for both Delaunay
// and non Delaunay triangulations
{
@ -244,6 +252,7 @@ _test_cls_triangulation_3(const Triangulation &)
assert(T0.dimension() == 1);
assert(T0.number_of_vertices() == 2);
assert(T0.is_valid());
assert(T0 != T0d0);
Cls T0d1(T0);
assert(T0 == T0d1);
@ -261,6 +270,7 @@ _test_cls_triangulation_3(const Triangulation &)
assert(T0.dimension() == 2);
assert(T0.number_of_vertices() == 3);
assert(T0.is_valid());
assert(T0 != T0d1);
Cls T0d2(T0);
assert(T0 == T0d2);
@ -278,6 +288,7 @@ _test_cls_triangulation_3(const Triangulation &)
assert(T0.dimension() == 3);
assert(T0.number_of_vertices() == 4);
assert(T0.is_valid());
assert(T0 != T0d2);
Cls T0d3(T0);
assert(T0 == T0d3);