diff --git a/Triangulation_2/include/CGAL/Triangulation_2.h b/Triangulation_2/include/CGAL/Triangulation_2.h index 3d2a6e73629..9c20817b5d6 100644 --- a/Triangulation_2/include/CGAL/Triangulation_2.h +++ b/Triangulation_2/include/CGAL/Triangulation_2.h @@ -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 &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& t1, + const Triangulation_2& t2, + typename Triangulation_2::Face_handle f1, + typename Triangulation_2::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 Tr1; + typedef Triangulation_2 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 > 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& t1, + const Triangulation_2& t2) +{ + typedef typename Triangulation_2::Vertex_handle Vertex_handle1; + typedef typename Triangulation_2::Face_handle Face_handle1; + typedef typename Triangulation_2::Vertex_handle Vertex_handle2; + typedef typename Triangulation_2::Face_handle Face_handle2; + + typedef typename Triangulation_2::Point Point; + + typedef typename Triangulation_2::Geom_traits::Equal_2 Equal_2; + typedef typename Triangulation_2::Geom_traits::Compare_xy_2 Compare_xy_2; + typedef typename Triangulation_2::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 V1 (t1.points_begin(), t1.points_end()); + std::vector 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 Vmap; + std::unordered_map 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 ifs; + auto fc = t2.incident_faces(iv2), done(fc); + do { + ifs.push_back(fc); + } while(++fc != done); + + for(typename std::vector::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& t1, + const Triangulation_2& t2) +{ + return ! (t1 == t2); +} + } //namespace CGAL #include diff --git a/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_triangulation_2.h b/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_triangulation_2.h index 15caad00e68..0aee5fb2e42 100644 --- a/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_triangulation_2.h +++ b/Triangulation_2/test/Triangulation_2/include/CGAL/_test_cls_triangulation_2.h @@ -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 *******/ diff --git a/Triangulation_3/include/CGAL/Triangulation_3.h b/Triangulation_3/include/CGAL/Triangulation_3.h index 25d4f462201..f075bfdeb5e 100644 --- a/Triangulation_3/include/CGAL/Triangulation_3.h +++ b/Triangulation_3/include/CGAL/Triangulation_3.h @@ -24,14 +24,6 @@ # include #endif -#include -#include -#include -#include -#include -#include -#include - #include #include #include @@ -77,6 +69,14 @@ # include #endif +#include +#include +#include +#include +#include +#include +#include + #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& t1, const Triangulation_3& t2, typename Triangulation_3::Cell_handle c1, typename Triangulation_3::Cell_handle c2, - std::map::Cell_handle, - typename Triangulation_3::Cell_handle>& Cmap, - std::map::Vertex_handle, - typename Triangulation_3::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& t1, typedef typename Tr2::Vertex_handle Vertex_handle2; typedef typename Tr2::Cell_handle Cell_handle2; - typedef typename std::map::const_iterator Vit; - typedef typename std::map::const_iterator Cit; + typedef typename std::unordered_map::const_iterator Vit; + typedef typename std::unordered_map::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& t1, Construct_point_3 cp = t1.geom_traits().construct_point_3_object(); std::vector > 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& 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& 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& t1, // We will store the mapping between the 2 triangulations vertices and // cells in 2 maps. - std::map Vmap; - std::map Cmap; + std::unordered_map Vmap; + std::unordered_map 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& t1, } // Found it ! - Cmap.insert(std::make_pair(c, *cit)); + Cmap.emplace(c, *cit); break; } diff --git a/Triangulation_3/test/Triangulation_3/include/CGAL/_test_cls_triangulation_3.h b/Triangulation_3/test/Triangulation_3/include/CGAL/_test_cls_triangulation_3.h index fe5b2d041b2..9790692fd90 100644 --- a/Triangulation_3/test/Triangulation_3/include/CGAL/_test_cls_triangulation_3.h +++ b/Triangulation_3/test/Triangulation_3/include/CGAL/_test_cls_triangulation_3.h @@ -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);