From 0105eefe4543a35cc0026a431a958a4b349b8226 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Mon, 6 May 2019 16:07:46 +0200 Subject: [PATCH 1/9] Add a test to detect if the result of the slicer is cw oriented when seen from the positive side of the slice-plane. If it is, reverse the orientation. Add assertions in the test. --- .../include/CGAL/Polygon_mesh_slicer.h | 28 +++++++++ .../polygon_mesh_slicer_test.cpp | 63 ++++++++++++++++--- 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h index cda6a7e6c77..50409a19323 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h @@ -168,6 +168,30 @@ class Polygon_mesh_slicer {} std::vector< Point_3 > current_poly; + + template + bool is_polyline_cw_oriented(ForwardIterator first, + ForwardIterator last) + { + + ForwardIterator i = std::min_element(first, last, typename Traits::Less_xyz_3()); + + ForwardIterator prev = (i == first) ? last : i; + --prev; + + ForwardIterator next = i; + ++next; + if (next == last) + next = first; + + Point_3 p1(*prev), p2(*i), p3(*next); + return typename Traits::Orientation_3()(m_plane.orthogonal_vector(), + typename Traits::Vector_3(p1, p2), + typename Traits::Vector_3(p1, p3)) + == CGAL::CLOCKWISE; + + } + void start_new_polyline() { current_poly.clear(); @@ -197,6 +221,10 @@ class Polygon_mesh_slicer void end_polyline() { CGAL_assertion(!current_poly.empty()); + if(is_polyline_cw_oriented(current_poly.begin(), current_poly.end())) + { + std::reverse(current_poly.begin(), current_poly.end()); + } *out++=current_poly; } }; diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp index a08b5302938..1270c92a8f9 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp @@ -12,7 +12,7 @@ #include #include #include - +#include #include #include @@ -66,33 +66,76 @@ typedef std::list< Polyline_type > Polylines; Polylines polylines; // test isolated vertex - slicer(typename K::Plane_3(0,1,0,0), std::back_inserter(polylines)); + typename K::Plane_3 plane(0,1,0,0); + slicer(plane, std::back_inserter(polylines)); assert(polylines.size()==2); // two polylines assert( (polylines.front().size()==1) != (polylines.back().size()==1)); //only one isolated vertex - + + CGAL::Polygon_2 polygon; + for(auto p : polylines.back()) + { + polygon.push_back(plane.to_2d(p)); + } + + assert(polygon.is_counterclockwise_oriented()); //test two nested polylines, one open and one closed polylines.clear(); - slicer(typename K::Plane_3(0,1,0,0.5), std::back_inserter(polylines)); + polygon.clear(); + plane = typename K::Plane_3(0,1,0,0.5); + slicer(plane, std::back_inserter(polylines)); assert(polylines.size()==2);// two polylines assert( (polylines.front().front()==polylines.front().back()) != (polylines.back().front()==polylines.back().back()) ); //one open and one closed polyline - // test only coplanar edges + for(auto polyline : polylines) + { + for(auto p : polyline) + { + polygon.push_back(plane.to_2d(p)); + } + if(polygon.is_simple()) + assert(polygon.is_counterclockwise_oriented()); + polygon.clear(); + } polylines.clear(); - slicer(typename K::Plane_3(0,0,1,1), std::back_inserter(polylines)); + + // test only coplanar edges + plane = typename K::Plane_3(0,0,1,1); + slicer(plane, std::back_inserter(polylines)); assert(polylines.size()==1); // one polyline assert(polylines.front().front()==polylines.front().back()); // that is closed + - - //test only coplanar border edges + for(auto polyline : polylines) + { + for(auto p : polyline) + { + polygon.push_back(plane.to_2d(p)); + } + if(polygon.is_simple()) + assert(polygon.is_counterclockwise_oriented()); + polygon.clear(); + } polylines.clear(); - slicer(typename K::Plane_3(0,0,1,-1), std::back_inserter(polylines)); + + //test only coplanar border edges + plane = typename K::Plane_3(0,0,1,-1); + slicer(plane, std::back_inserter(polylines)); assert(polylines.size()==1); // one polyline assert(polylines.front().front()!=polylines.front().back()); // that is closed - //test no intersection + for(auto polyline : polylines) + { + for(auto p : polyline) + { + polygon.push_back(plane.to_2d(p)); + } + assert(polygon.is_counterclockwise_oriented()); + polygon.clear(); + } polylines.clear(); + //test no intersection slicer(typename K::Plane_3(0,0,1,333), std::back_inserter(polylines)); assert(polylines.empty()); From fe675e87b1d73da21f3e6c19962e6e6e489f9570 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Tue, 7 May 2019 09:58:27 +0200 Subject: [PATCH 2/9] Fix test and update doc. --- .../include/CGAL/Polygon_mesh_slicer.h | 1 + .../polygon_mesh_slicer_test.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h index 50409a19323..56b4492a42f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h @@ -413,6 +413,7 @@ public: /** * Constructs the intersecting polylines of `plane` with the input triangulated surface mesh. + * The resulting polylines are counterclockwise oriented when seen from the positive side of `plane`. * @tparam OutputIterator an output iterator accepting polylines. * A polyline is provided as `std::vector`. * A polyline is closed if its first and last point are identical. diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp index 1270c92a8f9..6089b79800e 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp @@ -94,8 +94,9 @@ typedef std::list< Polyline_type > Polylines; { polygon.push_back(plane.to_2d(p)); } - if(polygon.is_simple()) - assert(polygon.is_counterclockwise_oriented()); + if(polyline.front() == polyline.back()) + polygon.erase(polygon.vertices_end()-1); + assert(polygon.is_counterclockwise_oriented()); polygon.clear(); } polylines.clear(); @@ -113,8 +114,9 @@ typedef std::list< Polyline_type > Polylines; { polygon.push_back(plane.to_2d(p)); } - if(polygon.is_simple()) - assert(polygon.is_counterclockwise_oriented()); + if(polyline.front() == polyline.back()) + polygon.erase(polygon.vertices_end()-1); + assert(polygon.is_counterclockwise_oriented()); polygon.clear(); } polylines.clear(); From 29ba7a8992ebfb5f4b05aab290011cf4bed69eda Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Tue, 7 May 2019 10:19:44 +0200 Subject: [PATCH 3/9] check that there are at least 2 points in the polyline before testing --- Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h index 56b4492a42f..92892a87b39 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h @@ -221,7 +221,8 @@ class Polygon_mesh_slicer void end_polyline() { CGAL_assertion(!current_poly.empty()); - if(is_polyline_cw_oriented(current_poly.begin(), current_poly.end())) + if(current_poly.size()>1 + && is_polyline_cw_oriented(current_poly.begin(), current_poly.end())) { std::reverse(current_poly.begin(), current_poly.end()); } From cc4bc212f9d716d680750d56b905dd59f26c5077 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Tue, 7 May 2019 10:41:13 +0200 Subject: [PATCH 4/9] Don't use c++11 in tests --- .../polygon_mesh_slicer_test.cpp | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp index 6089b79800e..e1880273ba1 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp @@ -24,47 +24,47 @@ template int test_slicer() { #ifdef USE_SURFACE_MESH -typedef CGAL::Surface_mesh Mesh; + typedef CGAL::Surface_mesh Mesh; #else -typedef CGAL::Polyhedron_3 Mesh; + typedef CGAL::Polyhedron_3 Mesh; #endif - -typedef CGAL::AABB_halfedge_graph_segment_primitive HGSP; -typedef CGAL::AABB_traits AABB_traits; -typedef CGAL::AABB_tree AABB_tree; -typedef std::vector Polyline_type; -typedef std::list< Polyline_type > Polylines; - + + typedef CGAL::AABB_halfedge_graph_segment_primitive HGSP; + typedef CGAL::AABB_traits AABB_traits; + typedef CGAL::AABB_tree AABB_tree; + typedef std::vector Polyline_type; + typedef std::list< Polyline_type > Polylines; + //API test { std::ifstream input("data/U.off"); Mesh m; - + if (!input || !(input >> m)){ std::cerr << "Error: can not read file.\n"; return 1; } - + AABB_tree tree(edges(m).first, edges(m).second, m); - + CGAL::Polygon_mesh_slicer slicer(m, tree); Polylines polylines; slicer(typename K::Plane_3(1,1,0,0), std::back_inserter(polylines)); assert(polylines.size()==1); } - + std::ifstream input("data_slicer/open_cube_meshed.off"); Mesh m; - + if (!input || !(input >> m)){ std::cerr << "Error: can not read file.\n"; return 1; } - + CGAL::Polygon_mesh_slicer slicer(m); - + Polylines polylines; - + // test isolated vertex typename K::Plane_3 plane(0,1,0,0); slicer(plane, std::back_inserter(polylines)); @@ -72,13 +72,13 @@ typedef std::list< Polyline_type > Polylines; assert( (polylines.front().size()==1) != (polylines.back().size()==1)); //only one isolated vertex CGAL::Polygon_2 polygon; - for(auto p : polylines.back()) + BOOST_FOREACH(const typename K::Point_3& p, polylines.back()) { polygon.push_back(plane.to_2d(p)); } assert(polygon.is_counterclockwise_oriented()); - + //test two nested polylines, one open and one closed polylines.clear(); polygon.clear(); @@ -86,11 +86,12 @@ typedef std::list< Polyline_type > Polylines; slicer(plane, std::back_inserter(polylines)); assert(polylines.size()==2);// two polylines assert( (polylines.front().front()==polylines.front().back()) != - (polylines.back().front()==polylines.back().back()) ); //one open and one closed polyline - - for(auto polyline : polylines) + (polylines.back().front()==polylines.back().back()) ); //one open and one closed polyline + + + BOOST_FOREACH(const Polyline_type& polyline, polylines) { - for(auto p : polyline) + BOOST_FOREACH(const typename K::Point_3& p, polyline) { polygon.push_back(plane.to_2d(p)); } @@ -107,10 +108,10 @@ typedef std::list< Polyline_type > Polylines; assert(polylines.size()==1); // one polyline assert(polylines.front().front()==polylines.front().back()); // that is closed - - for(auto polyline : polylines) + + BOOST_FOREACH(const Polyline_type& polyline, polylines) { - for(auto p : polyline) + BOOST_FOREACH(const typename K::Point_3& p, polyline) { polygon.push_back(plane.to_2d(p)); } @@ -126,10 +127,10 @@ typedef std::list< Polyline_type > Polylines; slicer(plane, std::back_inserter(polylines)); assert(polylines.size()==1); // one polyline assert(polylines.front().front()!=polylines.front().back()); // that is closed - - for(auto polyline : polylines) + + BOOST_FOREACH(const Polyline_type& polyline, polylines) { - for(auto p : polyline) + BOOST_FOREACH(const typename K::Point_3& p, polyline) { polygon.push_back(plane.to_2d(p)); } @@ -140,7 +141,7 @@ typedef std::list< Polyline_type > Polylines; //test no intersection slicer(typename K::Plane_3(0,0,1,333), std::back_inserter(polylines)); assert(polylines.empty()); - + return 0; } @@ -149,6 +150,6 @@ int main() { assert(test_slicer() == 0); assert(test_slicer() == 0); - + return 0; } From a059b7aa3830ca1a499fbdf51779fa784a65560d Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Tue, 7 May 2019 10:52:18 +0200 Subject: [PATCH 5/9] don't touch polylines <=2 points --- Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h index 92892a87b39..96a2e69293c 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h @@ -221,7 +221,7 @@ class Polygon_mesh_slicer void end_polyline() { CGAL_assertion(!current_poly.empty()); - if(current_poly.size()>1 + if(current_poly.size()> 2 && is_polyline_cw_oriented(current_poly.begin(), current_poly.end())) { std::reverse(current_poly.begin(), current_poly.end()); From 962bac73b263073abd72f2a1f1bfa4f462e75f81 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Tue, 7 May 2019 11:12:59 +0200 Subject: [PATCH 6/9] don't test if not closed --- .../include/CGAL/Polygon_mesh_slicer.h | 3 +- .../polygon_mesh_slicer_test.cpp | 56 +++++++++---------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h index 96a2e69293c..7f0e4818904 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h @@ -222,6 +222,7 @@ class Polygon_mesh_slicer { CGAL_assertion(!current_poly.empty()); if(current_poly.size()> 2 + && current_poly.back() == current_poly.front() //only if polyline is closed && is_polyline_cw_oriented(current_poly.begin(), current_poly.end())) { std::reverse(current_poly.begin(), current_poly.end()); @@ -414,7 +415,7 @@ public: /** * Constructs the intersecting polylines of `plane` with the input triangulated surface mesh. - * The resulting polylines are counterclockwise oriented when seen from the positive side of `plane`. + * The resulting polylines, if closed, are counterclockwise oriented when seen from the positive side of `plane`. * @tparam OutputIterator an output iterator accepting polylines. * A polyline is provided as `std::vector`. * A polyline is closed if its first and last point are identical. diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp index e1880273ba1..70cd40dd325 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp @@ -20,6 +20,24 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic; typedef CGAL::Exact_predicates_inexact_constructions_kernel Epec; +template +bool is_ccw(const typename K::Plane_3& plane, + const Polyline_type& polyline) +{ + CGAL::Polygon_2 polygon; + if(polyline.front() == polyline.back()) + { + BOOST_FOREACH(const typename K::Point_3& p, polyline) + { + polygon.push_back(plane.to_2d(p)); + } + polygon.erase(polygon.vertices_end()-1); + if(polygon.size() > 2 && + polygon.is_counterclockwise_oriented()) + return polygon.is_counterclockwise_oriented(); + } + return true; +} template int test_slicer() { @@ -71,17 +89,13 @@ int test_slicer() assert(polylines.size()==2); // two polylines assert( (polylines.front().size()==1) != (polylines.back().size()==1)); //only one isolated vertex - CGAL::Polygon_2 polygon; - BOOST_FOREACH(const typename K::Point_3& p, polylines.back()) + BOOST_FOREACH(const Polyline_type& polyline, polylines) { - polygon.push_back(plane.to_2d(p)); + bool ok = is_ccw( plane,polyline); + assert(ok); } - - assert(polygon.is_counterclockwise_oriented()); - //test two nested polylines, one open and one closed polylines.clear(); - polygon.clear(); plane = typename K::Plane_3(0,1,0,0.5); slicer(plane, std::back_inserter(polylines)); assert(polylines.size()==2);// two polylines @@ -91,14 +105,8 @@ int test_slicer() BOOST_FOREACH(const Polyline_type& polyline, polylines) { - BOOST_FOREACH(const typename K::Point_3& p, polyline) - { - polygon.push_back(plane.to_2d(p)); - } - if(polyline.front() == polyline.back()) - polygon.erase(polygon.vertices_end()-1); - assert(polygon.is_counterclockwise_oriented()); - polygon.clear(); + bool ok = is_ccw( plane,polyline); + assert(ok); } polylines.clear(); @@ -111,14 +119,8 @@ int test_slicer() BOOST_FOREACH(const Polyline_type& polyline, polylines) { - BOOST_FOREACH(const typename K::Point_3& p, polyline) - { - polygon.push_back(plane.to_2d(p)); - } - if(polyline.front() == polyline.back()) - polygon.erase(polygon.vertices_end()-1); - assert(polygon.is_counterclockwise_oriented()); - polygon.clear(); + bool ok = is_ccw( plane,polyline); + assert(ok); } polylines.clear(); @@ -130,12 +132,8 @@ int test_slicer() BOOST_FOREACH(const Polyline_type& polyline, polylines) { - BOOST_FOREACH(const typename K::Point_3& p, polyline) - { - polygon.push_back(plane.to_2d(p)); - } - assert(polygon.is_counterclockwise_oriented()); - polygon.clear(); + bool ok = is_ccw( plane,polyline); + assert(ok); } polylines.clear(); //test no intersection From cfea874cc106c2ae0584a8cedac0037951fa8814 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Tue, 7 May 2019 11:24:30 +0200 Subject: [PATCH 7/9] take the right vectors --- Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h index 7f0e4818904..4c399c191fb 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h @@ -186,8 +186,8 @@ class Polygon_mesh_slicer Point_3 p1(*prev), p2(*i), p3(*next); return typename Traits::Orientation_3()(m_plane.orthogonal_vector(), - typename Traits::Vector_3(p1, p2), - typename Traits::Vector_3(p1, p3)) + typename Traits::Vector_3(p2, p1), + typename Traits::Vector_3(p2, p3)) == CGAL::CLOCKWISE; } From c49f173d83f4988eeedd640f2b1a6afa1b17d729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 8 May 2019 10:13:40 +0200 Subject: [PATCH 8/9] polyline orientation depend on supporting mesh --- .../include/CGAL/Polygon_mesh_slicer.h | 133 +++++++++++++---- .../polygon_mesh_slicer_test.cpp | 138 ++++++++++-------- 2 files changed, 184 insertions(+), 87 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h index 4c399c191fb..b904d4228e1 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_slicer.h @@ -41,6 +41,7 @@ #include #include +#include namespace CGAL { @@ -63,7 +64,7 @@ namespace CGAL { /// orthogonal to a frame axis, the non-null coefficient being 1 or -1. /// The default is `true`. /// -/// The implemenation of this class depends on the package \ref PkgAABB_treeSummary. +/// The implementation of this class depends on the package \ref PkgAABB_treeSummary. /// \todo Shall we document more in details what is required? /// `Traits` must provide: /// - `Plane_3` @@ -152,6 +153,7 @@ class Polygon_mesh_slicer VertexPointMap m_vpmap; typename Traits_::Intersect_3 intersect_3; OutputIterator out; + std::pair nodes_for_orient; Polyline_visitor( TriangleMesh& tmesh, AL_graph& al_graph, @@ -168,36 +170,102 @@ class Polygon_mesh_slicer {} std::vector< Point_3 > current_poly; - - template - bool is_polyline_cw_oriented(ForwardIterator first, - ForwardIterator last) + + // returns true iff the polyline is not correctly oriented + // Using the first edge is oriented such that the normal induced by the face + // containing the edge, the oriented edge and the plane orthogonal vector defines + // a direct orthogonal basis + bool do_reverse_polyline() { - - ForwardIterator i = std::min_element(first, last, typename Traits::Less_xyz_3()); - - ForwardIterator prev = (i == first) ? last : i; - --prev; - - ForwardIterator next = i; - ++next; - if (next == last) - next = first; - - Point_3 p1(*prev), p2(*i), p3(*next); - return typename Traits::Orientation_3()(m_plane.orthogonal_vector(), - typename Traits::Vector_3(p2, p1), - typename Traits::Vector_3(p2, p3)) - == CGAL::CLOCKWISE; - + if (current_poly.size() < 2) + return false; + + AL_vertex_info v1 = al_graph[nodes_for_orient.first]; + AL_vertex_info v2 = al_graph[nodes_for_orient.second]; + + if (const vertex_descriptor* vd1_ptr = boost::get(&v1) ) + { + if (const vertex_descriptor* vd2_ptr = boost::get(&v2) ) + { + CGAL_assertion( halfedge(*vd1_ptr, *vd2_ptr, m_tmesh).second ); + halfedge_descriptor h_opp = halfedge(*vd1_ptr, *vd2_ptr, m_tmesh).first; + if ( !is_border(h_opp, m_tmesh) ) + { + CGAL_assertion(source(h_opp, m_tmesh) == *vd1_ptr); + CGAL::Oriented_side os = m_plane.oriented_side( get(m_vpmap, target(next(h_opp, m_tmesh), m_tmesh) ) ); + if (os != CGAL::ON_ORIENTED_BOUNDARY) + return m_plane.oriented_side( get(m_vpmap, target(next(h_opp, m_tmesh), m_tmesh)) ) == CGAL::ON_NEGATIVE_SIDE; + } + h_opp = opposite(h_opp, m_tmesh); + if ( !is_border(h_opp, m_tmesh) ) + { + CGAL_assertion(source(h_opp, m_tmesh) == *vd2_ptr); + CGAL::Oriented_side os = m_plane.oriented_side( get(m_vpmap, target(next(h_opp, m_tmesh), m_tmesh) ) ); + if (os != CGAL::ON_ORIENTED_BOUNDARY) + return m_plane.oriented_side( get(m_vpmap, target(next(h_opp, m_tmesh), m_tmesh)) ) == CGAL::ON_POSITIVE_SIDE; + } + return false; // since coplanar edges are filtered out, we should never end up here. + } + else + { + // e2 is intersected in its interior + edge_descriptor e2 = boost::get(v2); + halfedge_descriptor h2 = halfedge(e2, m_tmesh); + if ( target(next(h2, m_tmesh), m_tmesh) != *vd1_ptr ) + h2=opposite(h2, m_tmesh); + return m_plane.oriented_side( get(m_vpmap, source(h2, m_tmesh)) ) == CGAL::ON_POSITIVE_SIDE; + } + } + else + { + edge_descriptor e1 = boost::get(v1); + halfedge_descriptor h1 = halfedge(e1, m_tmesh); + if (const vertex_descriptor* vd2_ptr = boost::get(&v2) ) + { + // e1 is intersected in its interior + if ( target(next(h1, m_tmesh), m_tmesh) != *vd2_ptr ) + h1=opposite(h1, m_tmesh); + CGAL_assertion( target(next(h1, m_tmesh), m_tmesh) == *vd2_ptr ); + } + else + { + // intersection in the interior of both edges + edge_descriptor e2 = boost::get(v2); + halfedge_descriptor h2 = halfedge(e2, m_tmesh); + if ( face(h1, m_tmesh) != face(h2,m_tmesh) ) + { + halfedge_descriptor opp_h1 = opposite(h1, m_tmesh), + opp_h2 = opposite(h2, m_tmesh); + if ( face(opp_h1, m_tmesh) == face(opp_h2, m_tmesh) ) + { + h1=opp_h1; + h2=opp_h2; + } + else + if ( face(opp_h1, m_tmesh)==face(h2,m_tmesh) ) + h1=opp_h1; + else + h2=opp_h2; + } + CGAL_assertion( face(h1, m_tmesh) == face(h2,m_tmesh) ); + } + return m_plane.oriented_side( get(m_vpmap, source(h1, m_tmesh)) ) == CGAL::ON_NEGATIVE_SIDE; + } } - + void start_new_polyline() { current_poly.clear(); } + void add_node(AL_vertex_descriptor node_id) { + if (current_poly.empty()) + nodes_for_orient.first=node_id; + else + if (current_poly.size()==1) + nodes_for_orient.second=node_id; + AL_vertex_info v = al_graph[node_id]; if (const vertex_descriptor* vd_ptr = boost::get(&v) ) { @@ -221,12 +289,8 @@ class Polygon_mesh_slicer void end_polyline() { CGAL_assertion(!current_poly.empty()); - if(current_poly.size()> 2 - && current_poly.back() == current_poly.front() //only if polyline is closed - && is_polyline_cw_oriented(current_poly.begin(), current_poly.end())) - { + if(do_reverse_polyline()) std::reverse(current_poly.begin(), current_poly.end()); - } *out++=current_poly; } }; @@ -415,7 +479,18 @@ public: /** * Constructs the intersecting polylines of `plane` with the input triangulated surface mesh. - * The resulting polylines, if closed, are counterclockwise oriented when seen from the positive side of `plane`. + * + * If a polyline is closed, the first and last point of that polyline will be identical. + * + * Each resulting polyline `P` is oriented such that for two consecutive points `p` and `q` in `P`, + * the normal vector of the face(s) containing the segment `pq`, the vector `pq`, and the orthogonal vertor of `plane` + * is a direct orthogonal basis. + * The normal vector of each face is chosen to point on the side of the face + * where its sequence of vertices is seen counterclockwise. + * + * Note that an edge shared by two faces included in `plane` will not be reported. For example, + * if `plane` passes though one face of a cube, only one closed polyline will be reported (the boundary of the face) + * * @tparam OutputIterator an output iterator accepting polylines. * A polyline is provided as `std::vector`. * A polyline is closed if its first and last point are identical. diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp index 70cd40dd325..38cd7a0ad9e 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -21,7 +22,7 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel Epic; typedef CGAL::Exact_predicates_inexact_constructions_kernel Epec; template -bool is_ccw(const typename K::Plane_3& plane, +bool is_ccw(int xi, int yi, const Polyline_type& polyline) { CGAL::Polygon_2 polygon; @@ -29,12 +30,10 @@ bool is_ccw(const typename K::Plane_3& plane, { BOOST_FOREACH(const typename K::Point_3& p, polyline) { - polygon.push_back(plane.to_2d(p)); + polygon.push_back(typename K::Point_2(p[xi], p[yi])); } polygon.erase(polygon.vertices_end()-1); - if(polygon.size() > 2 && - polygon.is_counterclockwise_oriented()) - return polygon.is_counterclockwise_oriented(); + return polygon.is_counterclockwise_oriented(); } return true; } @@ -46,100 +45,123 @@ int test_slicer() #else typedef CGAL::Polyhedron_3 Mesh; #endif - + typedef CGAL::AABB_halfedge_graph_segment_primitive HGSP; typedef CGAL::AABB_traits AABB_traits; typedef CGAL::AABB_tree AABB_tree; typedef std::vector Polyline_type; - typedef std::list< Polyline_type > Polylines; - + typedef std::vector< Polyline_type > Polylines; + //API test { std::ifstream input("data/U.off"); Mesh m; - + if (!input || !(input >> m)){ std::cerr << "Error: can not read file.\n"; return 1; } - + AABB_tree tree(edges(m).first, edges(m).second, m); - + CGAL::Polygon_mesh_slicer slicer(m, tree); Polylines polylines; slicer(typename K::Plane_3(1,1,0,0), std::back_inserter(polylines)); assert(polylines.size()==1); } - + std::ifstream input("data_slicer/open_cube_meshed.off"); Mesh m; - + if (!input || !(input >> m)){ std::cerr << "Error: can not read file.\n"; return 1; } - + CGAL::Polygon_mesh_slicer slicer(m); - + Polylines polylines; - + // test isolated vertex - typename K::Plane_3 plane(0,1,0,0); - slicer(plane, std::back_inserter(polylines)); + slicer(typename K::Plane_3(0,1,0,0), std::back_inserter(polylines)); assert(polylines.size()==2); // two polylines assert( (polylines.front().size()==1) != (polylines.back().size()==1)); //only one isolated vertex - - BOOST_FOREACH(const Polyline_type& polyline, polylines) - { - bool ok = is_ccw( plane,polyline); - assert(ok); - } + + //test two nested polylines, one open and one closed polylines.clear(); - plane = typename K::Plane_3(0,1,0,0.5); - slicer(plane, std::back_inserter(polylines)); + slicer(typename K::Plane_3(0,1,0,0.5), std::back_inserter(polylines)); assert(polylines.size()==2);// two polylines assert( (polylines.front().front()==polylines.front().back()) != - (polylines.back().front()==polylines.back().back()) ); //one open and one closed polyline - - - BOOST_FOREACH(const Polyline_type& polyline, polylines) - { - bool ok = is_ccw( plane,polyline); - assert(ok); - } - polylines.clear(); - + (polylines.back().front()==polylines.back().back()) ); //one open and one closed polyline + // test only coplanar edges - plane = typename K::Plane_3(0,0,1,1); - slicer(plane, std::back_inserter(polylines)); + polylines.clear(); + slicer(typename K::Plane_3(0,0,1,1), std::back_inserter(polylines)); assert(polylines.size()==1); // one polyline assert(polylines.front().front()==polylines.front().back()); // that is closed - - - BOOST_FOREACH(const Polyline_type& polyline, polylines) - { - bool ok = is_ccw( plane,polyline); - assert(ok); - } - polylines.clear(); - + + //test only coplanar border edges - plane = typename K::Plane_3(0,0,1,-1); - slicer(plane, std::back_inserter(polylines)); + polylines.clear(); + slicer(typename K::Plane_3(0,0,1,-1), std::back_inserter(polylines)); assert(polylines.size()==1); // one polyline assert(polylines.front().front()!=polylines.front().back()); // that is closed - - BOOST_FOREACH(const Polyline_type& polyline, polylines) - { - bool ok = is_ccw( plane,polyline); - assert(ok); - } - polylines.clear(); + //test no intersection + polylines.clear(); slicer(typename K::Plane_3(0,0,1,333), std::back_inserter(polylines)); assert(polylines.empty()); - + +// Now test the orientation of polylines + polylines.clear(); + slicer(typename K::Plane_3(0,1,0,0.5), std::back_inserter(polylines)); + assert(polylines.size()==2); // two polylines + int closed_id = polylines.front().front()==polylines.front().back() ? 0 : 1; + CGAL_assertion( is_ccw(0, 2 , polylines[closed_id]) ); + + polylines.clear(); + slicer(typename K::Plane_3(0,-1,0,-0.5), std::back_inserter(polylines)); + assert(polylines.size()==2); // two polylines + closed_id = polylines.front().front()==polylines.front().back() ? 0 : 1; + CGAL_assertion( !is_ccw(0, 2, polylines[closed_id]) ); + + polylines.clear(); + slicer(typename K::Plane_3(0,0,1,1), std::back_inserter(polylines)); + assert(polylines.size()==1); // one polyline + CGAL_assertion( is_ccw(0, 1 , polylines[0]) ); + + polylines.clear(); + slicer(typename K::Plane_3(0,0,-1,-1), std::back_inserter(polylines)); + assert(polylines.size()==1); // one polyline + CGAL_assertion( !is_ccw(0, 1 , polylines[0]) ); + + // reverse face orientation (no need to rebuild the tree) + CGAL::Polygon_mesh_processing::reverse_face_orientations(m); + polylines.clear(); + slicer(typename K::Plane_3(0,1,0,0.5), std::back_inserter(polylines)); + assert(polylines.size()==2); // two polylines + closed_id = polylines.front().front()==polylines.front().back() ? 0 : 1; + CGAL_assertion( !is_ccw(0, 2 , polylines[closed_id]) ); + + polylines.clear(); + slicer(typename K::Plane_3(0,-1,0,-0.5), std::back_inserter(polylines)); + assert(polylines.size()==2); // two polylines + closed_id = polylines.front().front()==polylines.front().back() ? 0 : 1; + CGAL_assertion( is_ccw(0, 2, polylines[closed_id]) ); + + polylines.clear(); + slicer(typename K::Plane_3(0,0,1,1), std::back_inserter(polylines)); + assert(polylines.size()==1); // one polyline + CGAL_assertion( !is_ccw(0, 1 , polylines[0]) ); + + polylines.clear(); + slicer(typename K::Plane_3(0,0,-1,-1), std::back_inserter(polylines)); + assert(polylines.size()==1); // one polyline + CGAL_assertion( is_ccw(0, 1 , polylines[0]) ); + + + return 0; } @@ -148,6 +170,6 @@ int main() { assert(test_slicer() == 0); assert(test_slicer() == 0); - + return 0; } From 910d6d076b51b759c6e88dcc9433205ade08d199 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Mon, 13 May 2019 16:40:07 +0200 Subject: [PATCH 9/9] use CGAL_USE() --- .../test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp index 38cd7a0ad9e..bd1700284aa 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/polygon_mesh_slicer_test.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -118,6 +119,7 @@ int test_slicer() slicer(typename K::Plane_3(0,1,0,0.5), std::back_inserter(polylines)); assert(polylines.size()==2); // two polylines int closed_id = polylines.front().front()==polylines.front().back() ? 0 : 1; + CGAL_USE(closed_id); CGAL_assertion( is_ccw(0, 2 , polylines[closed_id]) ); polylines.clear();