From fa507ef86380ce4c70728232900942c81c34bc2f Mon Sep 17 00:00:00 2001 From: Guy Zucker Date: Thu, 5 Feb 2009 15:39:55 +0000 Subject: [PATCH] implemented polygon validation as global functions in Gps_polygon_validation.h instead of using afunctor from the GpsTraits2 model --- .gitattributes | 11 + .../Gps_polygon_validation.h | 769 ++++++++++++++---- .../CGAL/Boolean_set_operations_2/Gps_utils.h | 22 +- .../include/CGAL/General_polygon_set_2.h | 54 +- .../include/CGAL/Gps_segment_traits_2.h | 6 +- .../include/CGAL/Gps_traits_2.h | 7 +- .../include/CGAL/connect_holes.h | 1 + .../agg_op_test_suite_generator.cpp | 3 +- .../bop_test_suite_generator.cpp | 15 +- .../data/validation/val_test1.dat | 16 + .../data/validation/val_test2.dat | 18 + .../data/validation/val_test3.dat | 19 + .../data/validation/val_test4.dat | 18 + .../data/validation/val_test5.dat | 18 + .../data/validation/val_test6.dat | 15 + .../data/validation/val_test7.dat | 12 + .../data/validation/val_test8.dat | 12 + .../data/validation/val_test9.dat | 19 + .../validation/validation_test_output.txt | 0 .../test_polygon_validation.cpp | 133 +++ 20 files changed, 938 insertions(+), 230 deletions(-) create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test1.dat create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test2.dat create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test3.dat create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test4.dat create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test5.dat create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test6.dat create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test7.dat create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test8.dat create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test9.dat create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/validation_test_output.txt create mode 100644 Boolean_set_operations_2/test/Boolean_set_operations_2/test_polygon_validation.cpp diff --git a/.gitattributes b/.gitattributes index 0ed5bfe126e..b9cde230eee 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1255,9 +1255,20 @@ Boolean_set_operations_2/test/Boolean_set_operations_2/data/pgn_holes2.dat -text Boolean_set_operations_2/test/Boolean_set_operations_2/data/pgn_holes3.dat -text Boolean_set_operations_2/test/Boolean_set_operations_2/data/pgn_holes4.dat -text Boolean_set_operations_2/test/Boolean_set_operations_2/data/pgn_holes5.dat -text +Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test1.dat -text +Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test2.dat -text +Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test3.dat -text +Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test4.dat -text +Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test5.dat -text +Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test6.dat -text +Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test7.dat -text +Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test8.dat -text +Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test9.dat -text +Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/validation_test_output.txt -text Boolean_set_operations_2/test/Boolean_set_operations_2/test_agg_op.cmd eol=lf Boolean_set_operations_2/test/Boolean_set_operations_2/test_bop.cmd eol=lf Boolean_set_operations_2/test/Boolean_set_operations_2/test_connect_holes.cpp -text +Boolean_set_operations_2/test/Boolean_set_operations_2/test_polygon_validation.cpp -text Box_intersection_d/doc_tex/Box_intersection_d/fig/benchmark.eps -text svneol=unset#application/postscript Box_intersection_d/doc_tex/Box_intersection_d/fig/benchmark.gif -text svneol=unset#image/gif Box_intersection_d/doc_tex/Box_intersection_d/fig/benchmark.pdf -text svneol=unset#application/pdf diff --git a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_polygon_validation.h b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_polygon_validation.h index 2ac1056a127..14c08034a75 100644 --- a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_polygon_validation.h +++ b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_polygon_validation.h @@ -1,4 +1,4 @@ -// Copyright (c) 2005 Tel-Aviv University (Israel). +// Copyright (c) 2008 Tel-Aviv University (Israel). // All rights reserved. // // This file is part of CGAL (www.cgal.org); you may redistribute it under @@ -11,37 +11,87 @@ // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // -// $URL$ -// $Id$ // // -// Author(s) : Baruch Zukerman +// Author(s): Baruch Zukerman // Ron Wein +// Boris Kozorovitzky +// Guy Zucker -#ifndef CGAL_GPS_POLYGON_VALIDATION_VISITOR_H -#define CGAL_GPS_POLYGON_VALIDATION_VISITOR_H +#ifndef CGAL_GPS_POLYGON_VALIDATION_2_H +#define CGAL_GPS_POLYGON_VALIDATION_2_H #include #include #include #include #include +#include +#include +#include +#include +#include +#include + + +#include + +/*Arrangement is templated with extended face dcel*/ + template + class ValidationOverlayTraits : public CGAL::Arr_default_overlay_traits +{ +public: + typedef CGAL::Arr_default_overlay_traits Base; + typedef typename Base::Face_handle_A Face_handle_A; + typedef typename Base::Face_handle_B Face_handle_B; + typedef typename Base::Face_handle_R Face_handle_R; + + typedef typename Arrangement_2::Ccb_halfedge_const_circulator Ccb_halfedge_const_circulator; + typedef typename Arrangement_2::Halfedge_const_handle Halfedge_const_handle; + typedef typename Arrangement_2::Face_const_handle Face_const_handle; + typedef typename Arrangement_2::Hole_const_iterator Hole_const_iterator; + + /*red faces source is the arrangement of holes. The blue faces (face) are caused by the PWH's outer boundary*/ + virtual void create_face(Face_handle_A red_face, Face_handle_B blue_face, Face_handle_R r_face) { + if ((red_face->contained()==true) && (blue_face->contained()==false)) { + hole_overlap=true; + } + } + +public: + ValidationOverlayTraits() : hole_overlap(false) {} + bool getHoleOverlap() { + return hole_overlap; + } + void setHoleOverlap(bool b) { + hole_overlap=b; return; + } +private: + bool hole_overlap; +}; + CGAL_BEGIN_NAMESPACE -/*! \class +#define CGAL_GPS_POLYGON_VALIDATION_2_TYPEDEF \ + typedef Gps_traits_adaptor Traits_adapter_2;\ + typedef typename Traits_2::Curve_const_iterator Curve_const_iterator;\ + typedef std::pair Cci_pair;\ + typedef typename Traits_2::Construct_curves_2 Construct_curves_2;\ + typedef typename Traits_adapter_2::Construct_vertex_2 Construct_vertex_2; + + + /*! \class * A visitor used for checking whether the edges of a polygon are * non-intersecting. */ template - class Gps_polygon_validation_visitor : public Sweep_line_empty_visitor { private: typedef ArrTraits_ Traits_2; - typedef Gps_polygon_validation_visitor Self; typedef typename Traits_2::X_monotone_curve_2 X_monotone_curve_2; typedef typename Traits_2::Point_2 Point_2; @@ -105,118 +155,26 @@ protected: }; -/*! \class - * An uxiliary functor used for checking the validity of polygons. - */ -template -class Is_valid_2 -{ -public: - typedef Traits_ Traits_2; - typedef TraitsAdapter_ Traits_adapter_2; - -private: +//Traits_2 templates the General_polygon_set_2 Traits. +//These include types for polygon and PWH. + template + bool is_closed_polygon(const typename Traits_2::Polygon_2& pgn, Traits_2 traits) + { - typedef typename Traits_2::Point_2 Point_2; - typedef typename Traits_2::X_monotone_curve_2 X_monotone_curve_2; - typedef typename Traits_2::Polygon_2 Polygon_2; - typedef typename Traits_2::Polygon_with_holes_2 Polygon_with_holes_2; - typedef typename Polygon_with_holes_2::Hole_const_iterator - Hole_const_iterator; - typedef typename Traits_2::Curve_const_iterator Curve_const_iterator; - typedef std::pair Cci_pair; - typedef typename Traits_2::Construct_curves_2 Construct_curves_2; - - typedef typename Traits_adapter_2::Construct_vertex_2 - Construct_vertex_2; - typedef typename Traits_adapter_2::Orientation_2 Check_orientation_2; - - typedef Gps_polygon_validation_visitor Visitor; - typedef Sweep_line_2 Sweep_line ; - -private: - - // Data members: - Construct_curves_2 construct_curves_func; - Construct_vertex_2 construct_vertex_func; - Check_orientation_2 check_orientation_func; - -public: - - /*! Constructor. */ - Is_valid_2 (Traits_2& traits, - const Traits_adapter_2& tr_adapter) - { - construct_curves_func = traits.construct_curves_2_object(); - construct_vertex_func = tr_adapter.construct_vertex_2_object(); - check_orientation_func = tr_adapter.orientation_2_object(); - } - - /*! Check if the given polygon is valid. */ - bool operator()(const Polygon_2& pgn) - { - bool is_closed = _is_closed(pgn); - CGAL_warning_msg (is_closed, - "The polygon's boundary is not closed."); - if (! is_closed) - return (false); - - bool has_valid_orientation = _has_valid_orientation(pgn); - CGAL_warning_msg (has_valid_orientation, - "The polygon has a wrong orientation."); - if (! has_valid_orientation) - return (false); - - bool is_strictly_simple = _is_strictly_simple(pgn); - CGAL_warning_msg (is_strictly_simple, - "The polygon is not strictly simple."); - if (! is_strictly_simple) - return (false); - - return (true); - } - - /*! Check if the given polygon with holes is valid. */ - bool operator()(const Polygon_with_holes_2& pgn) - { - bool is_closed = _is_closed(pgn); - CGAL_warning_msg (is_closed, - "The polygon's boundary is not closed."); - if (! is_closed) - return (false); - - bool has_valid_orientation = _has_valid_orientation(pgn); - CGAL_warning_msg (has_valid_orientation, - "The polygon has a wrong orientation."); - if (! has_valid_orientation) - return (false); - - bool is_simple = _is_simple(pgn); - CGAL_warning_msg (is_simple, - "The polygon is not simple."); - if (! is_simple) - return (false); - - return (true); - } - -protected: - - bool _is_closed(const Polygon_2& pgn) - { - Cci_pair itr_pair = construct_curves_func (pgn); + CGAL_GPS_POLYGON_VALIDATION_2_TYPEDEF + Cci_pair itr_pair = traits.construct_curves_2_object()(pgn); Curve_const_iterator begin = itr_pair.first; Curve_const_iterator end = itr_pair.second; if (begin == end) return (true); // An empty polygon is valid. - Traits_2 tr; - typename Traits_2::Equal_2 equal_func = tr.equal_2_object(); + Traits_adapter_2 traits_adapter; + typename Traits_2::Equal_2 equal_func = traits.equal_2_object(); Curve_const_iterator curr, next; - + Construct_vertex_2 construct_vertex_func; + construct_vertex_func = traits_adapter.construct_vertex_2_object(); curr = next = begin; ++next; @@ -250,30 +208,16 @@ protected: return (true); } + template + bool is_simple_polygon(const typename Traits_2::Polygon_2& pgn,Traits_2 traits) + {// Previously known as is_strictly_simple - bool _is_closed (const Polygon_with_holes_2& pgn) - { - Traits_2 traits; - - if(! _is_closed (traits.construct_outer_boundary_object()(pgn))) - return (false); - - Hole_const_iterator itr; - std::pair pair = traits.construct_holes_object()(pgn); - for (itr = pair.first; itr!=pair.second; ++itr) - //for (itr = pgn.holes_begin(); itr != pgn.holes_end(); ++itr) - { - if(! _is_closed (*itr)) - return (false); - } - return (true); - } - - bool _is_strictly_simple (const Polygon_2& pgn) - { + CGAL_GPS_POLYGON_VALIDATION_2_TYPEDEF // Sweep the boundary curves and look for intersections. - Cci_pair itr_pair = construct_curves_func (pgn); - Traits_2 traits; + typedef Gps_polygon_validation_visitor Visitor; + typedef Sweep_line_2 Sweep_line; + + Cci_pair itr_pair = traits.construct_curves_2_object()(pgn); Visitor visitor; Sweep_line sweep_line (&traits, &visitor); @@ -281,53 +225,329 @@ protected: return (visitor.is_valid()); } - bool _is_simple (const Polygon_with_holes_2& pgn) + + template + bool has_valid_orientation_polygon (const typename Traits_2::Polygon_2& pgn,Traits_2 traits) { - // Construct a container of all boundary curves. - Traits_2 traits; - Polygon_2 pgn2 = traits.construct_outer_boundary_object()(pgn); - Cci_pair itr_pair = construct_curves_func(pgn2); - - std::list curves; - std::copy (itr_pair.first, itr_pair.second, - std::back_inserter(curves)); + CGAL_GPS_POLYGON_VALIDATION_2_TYPEDEF - std::pair pair = traits.construct_holes_object()(pgn); - Hole_const_iterator hoit; - for (hoit = pair.first; hoit!=pair.second; ++hoit) - //for (hoit = pgn.holes_begin(); hoit != pgn.holes_end(); ++hoit) - { - itr_pair = construct_curves_func (*hoit); - std::copy (itr_pair.first, itr_pair.second, - std::back_inserter(curves)); - } + Cci_pair itr_pair = traits.construct_curves_2_object()(pgn); + Traits_adapter_2 traits_adapter; + typedef typename Traits_adapter_2::Orientation_2 Check_orientation_2; - // Perform the sweep and check fir intersections. - //Traits_2 traits; moved to top, needed also for boundary. - Visitor visitor(false); - Sweep_line sweep_line (&traits, &visitor); - - visitor.sweep (curves.begin(), curves.end()); - return (visitor.is_valid()); - } - - bool _has_valid_orientation (const Polygon_2& pgn) - { - Cci_pair itr_pair = construct_curves_func (pgn); - if(itr_pair.first == itr_pair.second) return (true); // empty polygon - return (check_orientation_func (itr_pair.first, + return (traits_adapter.orientation_2_object()(itr_pair.first, itr_pair.second) == COUNTERCLOCKWISE); } - - bool _has_valid_orientation (const Polygon_with_holes_2& pgn) + /* A valid polygon is : + * 1 - Closed or empty polygon + * 2 - Simple (previously known as strictly simple) + * 3 - Counterclockwise oriented + */ + template + bool is_valid_polygon(const typename Traits_2::Polygon_2& pgn,Traits_2 traits) { + bool closed = is_closed_polygon(pgn,traits); + CGAL_warning_msg (closed, + "The polygon's boundary is not closed."); + if (! closed) + return (false); + + bool simple = is_simple_polygon(pgn,traits); + CGAL_warning_msg (simple, + "The polygon is not simple."); + if (!simple) + return (false); + + bool valid_orientation = has_valid_orientation_polygon(pgn,traits); + CGAL_warning_msg (valid_orientation, + "The polygon has a wrong orientation."); + if (! valid_orientation) + return (false); + + return (true); + } + + + template + bool is_closed_polygon_with_holes(const typename Traits_2::Polygon_with_holes_2& pgn, Traits_2 traits) + { + typedef typename Traits_2::Polygon_with_holes_2 Polygon_with_holes_2; + if(! is_closed_polygon (pgn.outer_boundary(),traits)) + return (false); + + typename Polygon_with_holes_2::Hole_const_iterator itr; + + for (itr = pgn.holes_begin(); itr != pgn.holes_end(); ++itr) + { + if(! is_closed_polygon (*itr,traits)) + return (false); + } + return (true); + } + + + template + bool is_crossover_outer_boundary(const typename Traits_2::Polygon_with_holes_2& pgn, Traits_2 traits ) { + CGAL_GPS_POLYGON_VALIDATION_2_TYPEDEF + typedef typename Traits_2::Point_2 Point_2; + typedef typename Traits_2::Compare_endpoints_xy_2 Compare_endpoints_xy_2; + typedef typename Traits_2::Construct_min_vertex_2 Construct_min_vertex_2; + typedef typename Traits_2::Construct_max_vertex_2 Construct_max_vertex_2; + typedef CGAL::Gps_default_dcel dcel; + typedef CGAL::General_polygon_set_2 Polygon_set_2; + typedef typename Traits_2::Polygon_with_holes_2 Polygon_with_holes_2; + typedef typename Polygon_set_2::Arrangement_2 Arrangement_2; + typedef typename Arrangement_2::Halfedge_handle Halfedge_handle; + typedef typename Arrangement_2::Vertex_handle Vertex_handle; + typedef typename Arrangement_2::Vertex_const_handle Vertex_const_handle; + typedef typename Traits_2::Curve_const_iterator Curve_const_iterator; + typedef CGAL::Arr_naive_point_location Naive_pl; + + typename std::list he_path; + typename std::list::iterator he_itr; + //functors used throughout the function + Construct_min_vertex_2 min_functor = traits.construct_min_vertex_2_object(); + Construct_max_vertex_2 max_functor = traits.construct_max_vertex_2_object(); + Compare_endpoints_xy_2 cmp_endpoints = traits.compare_endpoints_xy_2_object(); + + Cci_pair itr_pair = traits.construct_curves_2_object()(pgn.outer_boundary()); + Curve_const_iterator begin = itr_pair.first; + Curve_const_iterator end = itr_pair.second; + if (begin == end) + return (true); // An empty polygon is valid. + //handles to consecutive curves + Curve_const_iterator curr, next; + curr = next = begin; + //handles to vertices for insert. one maintains the current curve (already inserted) and next curve's joint vertex. + //the other maintains the next curve's second vertex if it already exists in the arrangement. + Vertex_handle joint_ver, second_ver; + //closed check guarantees polygon has more than 1 curve + ++next; + //halfedge handle whose target is always the joint vertex between next and curr. + Halfedge_handle last_he; + + Polygon_set_2 gps(traits); + Arrangement_2 arr = gps.arrangement(); + Naive_pl pl (arr); + //insert first edge lexicographically to arrangement + // compute the joint vertex and insert to the path list a halfedge whose target is the joint vertex + last_he = CGAL::insert_non_intersecting_curve(arr, *curr); + if (cmp_endpoints(*curr) == SMALLER) { //polygon's boundary first curve is in lexicographic direction + joint_ver = last_he->target(); + he_path.push_back(last_he); + } else { //polygon's boundary first curve not lexicographic + joint_ver = last_he->source(); + he_path.push_back(last_he->twin()); + } + + /*insert the rest of the curves to the arrangement efficiently the previous closed polygon check guarantees + equal_func (construct_vertex_func (*curr, 1), construct_vertex_func (*next, 0))) */ + while (next != end) { + CGAL::Object obj; + Vertex_const_handle cver; + Point_2 second_point; + if(cmp_endpoints(*next) == SMALLER) { + //next curve's minimum is the joint vertex. Look if it's max exists in the arrangement and insert lexicographically + second_point = max_functor(*next); + obj = pl.locate(second_point); + if (CGAL::assign (cver, obj)) { + //insert where both vertices exist + second_ver = arr.non_const_handle(cver); + last_he = arr.insert_at_vertices( *next, joint_ver, second_ver); + } else //insert from left vertex + last_he = arr.insert_from_left_vertex ( *next,joint_ver) ; + } else { //next curve's maximum vertex is the joint vertex. try to locate the min vertex, and insert from right or from both vertices + second_point = min_functor(*next); + obj = pl.locate(second_point); + if (CGAL::assign (cver, obj)) { + //insert where both vertices exist + second_ver = arr.non_const_handle(cver); + last_he = arr.insert_at_vertices( *next, joint_ver, second_ver); + } else //insert from right vertex + last_he = arr.insert_from_right_vertex ( *next,joint_ver) ; + } + // Move to the next pair of edges. + he_path.push_back(last_he); + joint_ver=last_he->target(); + curr = next; + ++next; + } //end of while + /*We created a path of halfedges that circulates the polygon counterclockwise (curves . The + polygon should ly on the left of each of these half edges. If the boundary is invalid, the unbounded face should + be on the left of one of more than one of the halfedges*/ + typename Arrangement_2::Face_const_handle cfh; + typename Arrangement_2::Face_handle fh; + cfh = arr.unbounded_face(); + fh = arr.non_const_handle(cfh); + for (he_itr = he_path.begin();he_itr != he_path.end();he_itr++) { + Halfedge_handle cur = *he_itr; + if (cur->face() == fh) + return false; + } + return true; + } + + + + + //templated point location version + template + bool is_crossover_outer_boundary(const typename Traits_2::Polygon_with_holes_2& pgn, Traits_2 traits, const PointLocation& pl ) { + CGAL_GPS_POLYGON_VALIDATION_2_TYPEDEF + typedef typename Traits_2::Point_2 Point_2; + typedef typename Traits_2::Compare_endpoints_xy_2 Compare_endpoints_xy_2; + typedef typename Traits_2::Construct_min_vertex_2 Construct_min_vertex_2; + typedef typename Traits_2::Construct_max_vertex_2 Construct_max_vertex_2; + typedef CGAL::Gps_default_dcel dcel; + typedef CGAL::General_polygon_set_2 Polygon_set_2; + typedef typename Traits_2::Polygon_with_holes_2 Polygon_with_holes_2; + typedef typename Polygon_set_2::Arrangement_2 Arrangement_2; + typedef typename Arrangement_2::Halfedge_handle Halfedge_handle; + typedef typename Arrangement_2::Vertex_handle Vertex_handle; + typedef typename Arrangement_2::Vertex_const_handle Vertex_const_handle; + typedef typename Traits_2::Curve_const_iterator Curve_const_iterator; + typedef CGAL::Arr_naive_point_location Naive_pl; + + typename std::list he_path; + typename std::list::iterator he_itr; + //functors used throughout the function + Construct_min_vertex_2 min_functor = traits.construct_min_vertex_2_object(); + Construct_max_vertex_2 max_functor = traits.construct_max_vertex_2_object(); + Compare_endpoints_xy_2 cmp_endpoints = traits.compare_endpoints_xy_2_object(); + + Cci_pair itr_pair = traits.construct_curves_2_object()(pgn.outer_boundary()); + Curve_const_iterator begin = itr_pair.first; + Curve_const_iterator end = itr_pair.second; + if (begin == end) + return (true); // An empty polygon is valid. + //handles to consecutive curves + Curve_const_iterator curr, next; + curr = next = begin; + //handles to vertices for insert. one maintains the current curve (already inserted) and next curve's joint vertex. + //the other maintains the next curve's second vertex if it already exists in the arrangement. + Vertex_handle joint_ver, second_ver; + //closed check guarantees polygon has more than 1 curve + ++next; + //halfedge handle whose target is always the joint vertex between next and curr. + Halfedge_handle last_he; + + Polygon_set_2 gps(traits); + Arrangement_2 arr = gps.arrangement(); + //insert first edge lexicographically to arrangement + // compute the joint vertex and insert to the path list a halfedge whose target is the joint vertex + last_he = CGAL::insert_non_intersecting_curve(arr, *curr); + if (cmp_endpoints(*curr) == SMALLER) { //polygon's boundary first curve is in lexicographic direction + joint_ver = last_he->target(); + he_path.push_back(last_he); + } else { //polygon's boundary first curve not lexicographic + joint_ver = last_he->source(); + he_path.push_back(last_he->twin()); + } + + /*insert the rest of the curves to the arrangement efficiently the previous closed polygon check guarantees + equal_func (construct_vertex_func (*curr, 1), construct_vertex_func (*next, 0))) */ + while (next != end) { + CGAL::Object obj; + Vertex_const_handle cver; + Point_2 second_point; + if(cmp_endpoints(*next) == SMALLER) { + //next curve's minimum is the joint vertex. Look if it's max exists in the arrangement and insert lexicographically + second_point = max_functor(*next); + obj = pl.locate(second_point); + if (CGAL::assign (cver, obj)) { + //insert where both vertices exist + second_ver = arr.non_const_handle(cver); + last_he = arr.insert_at_vertices( *next, joint_ver, second_ver); + } else //insert from left vertex + last_he = arr.insert_from_left_vertex ( *next,joint_ver) ; + } else { //next curve's maximum vertex is the joint vertex. try to locate the min vertex, and insert from right or from both vertices + second_point = min_functor(*next); + obj = pl.locate(second_point); + if (CGAL::assign (cver, obj)) { + //insert where both vertices exist + second_ver = arr.non_const_handle(cver); + last_he = arr.insert_at_vertices( *next, joint_ver, second_ver); + } else //insert from right vertex + last_he = arr.insert_from_right_vertex ( *next,joint_ver) ; + } + // Move to the next pair of edges. + he_path.push_back(last_he); + joint_ver=last_he->target(); + curr = next; + ++next; + } //end of while + /*We created a path of halfedges that circulates the polygon counterclockwise (curves . The + polygon should ly on the left of each of these half edges. If the boundary is invalid, the unbounded face should + be on the left of one of more than one of the halfedges*/ + typename Arrangement_2::Face_const_handle cfh; + typename Arrangement_2::Face_handle fh; + cfh = arr.unbounded_face(); + fh = arr.non_const_handle(cfh); + for (he_itr = he_path.begin();he_itr != he_path.end();he_itr++) { + Halfedge_handle cur = *he_itr; + if (cur->face() == fh) + return false; + } + return true; + } + + + template + bool is_relatively_simple_polygon_with_holes(const typename Traits_2::Polygon_with_holes_2& pgn, Traits_2 traits) + {// previously known as Simple + + CGAL_GPS_POLYGON_VALIDATION_2_TYPEDEF + typedef typename Traits_2::X_monotone_curve_2 X_monotone_curve_2; + typedef Gps_polygon_validation_visitor Visitor; + typedef Sweep_line_2 Sweep_line; + typedef typename Traits_2::Polygon_with_holes_2 Polygon_with_holes_2; + + Construct_curves_2 construct_curves_func = traits.construct_curves_2_object(); + // Construct a container of all outer boundary curves. + Cci_pair itr_pair = construct_curves_func (pgn.outer_boundary()); + std::list outer_curves; + std::copy (itr_pair.first, itr_pair.second, + std::back_inserter(outer_curves)); + //create visitor and sweep to verify outer boundary is relatively simple + Visitor relative_visitor(false); + Sweep_line sweep_line (&traits, &relative_visitor); + relative_visitor.sweep (outer_curves.begin(), outer_curves.end()); + if (!relative_visitor.is_valid()) + return false; + + //verify every hole is simple + typename Polygon_with_holes_2::Hole_const_iterator hoit; + std::list hole_curves; + for (hoit = pgn.holes_begin(); hoit != pgn.holes_end(); ++hoit) + { + bool simple_hole = is_simple_polygon(*hoit,traits); + if (!simple_hole) + return false; + } + return true; + } + + + + template + bool has_valid_orientation_polygon_with_holes (const typename Traits_2::Polygon_with_holes_2& pgn, Traits_2 traits) + { + CGAL_GPS_POLYGON_VALIDATION_2_TYPEDEF + typedef typename Traits_2::X_monotone_curve_2 X_monotone_curve_2; + typedef Gps_polygon_validation_visitor Visitor; + typedef Sweep_line_2 Sweep_line; + typedef typename Traits_adapter_2::Orientation_2 Check_orientation_2; + typedef typename Traits_2::Polygon_with_holes_2 Polygon_with_holes_2; + + Traits_adapter_2 traits_adapter; + + Construct_curves_2 construct_curves_func = traits.construct_curves_2_object(); + Check_orientation_2 check_orientation_func = traits_adapter.orientation_2_object();; // Check the orientation of the outer boundary. - Traits_2 traits; - Polygon_2 pgn2 = traits.construct_outer_boundary_object()(pgn); - Cci_pair itr_pair = construct_curves_func (pgn2); + Cci_pair itr_pair = construct_curves_func (pgn.outer_boundary()); if ((itr_pair.first != itr_pair.second) && check_orientation_func (itr_pair.first, @@ -337,11 +557,9 @@ protected: } // Check the orientation of each of the holes. -// typename Polygon_with_holes_2:: - Hole_const_iterator hoit; - std::pair pair = traits.construct_holes_object()(pgn); - for (hoit = pair.first; hoit!=pair.second;++hoit) - //for (hoit = pgn.holes_begin(); hoit != pgn.holes_end(); ++hoit) + typename Polygon_with_holes_2::Hole_const_iterator hoit; + + for (hoit = pgn.holes_begin(); hoit != pgn.holes_end(); ++hoit) { itr_pair = construct_curves_func (*hoit); @@ -352,11 +570,206 @@ protected: return (false); } } - return (true); } -}; + + + + + +/*Verify holes do not intersect between themselves as well with the outer boundary +(except intersection on a vertex which is allowed). + +This efficient implementation utilizes the general poygon set for aggregated join +operations for N holes which should result in a GPS that contains N independent PWH. +Executing a difference(gps, outer boundary) should result in an empty set if +no holes intersect the boundary. + +An iterative use of the difference free function while iterating over the holes +may have an advantage in case there are numerous holes that intersect the boundary +and the iterative loop will be stopped after a small number of iterations. + +*/ + template + bool are_holes_and_boundary_pairwise_disjoint(const typename Traits_2::Polygon_with_holes_2& pwh, Traits_2 traits) + { + CGAL_GPS_POLYGON_VALIDATION_2_TYPEDEF + + typedef CGAL::Gps_default_dcel dcel; + typedef CGAL::General_polygon_set_2 Polygon_set_2; + typedef typename Polygon_set_2::Size Size; + typedef typename Traits_2::Polygon_2 Polygon_2; + typedef typename Traits_2::Polygon_with_holes_2 Polygon_with_holes_2; + typedef typename Polygon_with_holes_2::Hole_const_iterator Hole_const_iterator; + typedef typename Traits_2::X_monotone_curve_2 X_monotone_curve_2; + typedef std::pair Cci_pair; + typedef typename Traits_2::Construct_curves_2 Construct_curves_2; + + typedef typename Traits_adapter_2::Construct_vertex_2 + Construct_vertex_2; + typedef Gps_polygon_validation_visitor Visitor; + typedef Sweep_line_2 Sweep_line ; + typedef typename Polygon_set_2::Arrangement_2 Arrangement_2; + + /* Should be perfored more efficeintly than using sweep and than difference().*/ + + /*Use sweep to find intersections on the interior of curves (not on vertices) and overlapping edges which are not allowed + (note that 0/1 dimension intersections are not detectes by do_intersect() which only returns the 2D intersection polygon if exists) + Note that using this sweep alone allows for a hole and an edge to share a vertex and intersect + (like illegal input pgn_w_overlap_hole.dat in validation_example)*/ + Hole_const_iterator hoit; + // Construct a container of all boundary curves. + Polygon_2 pgn2 = traits.construct_outer_boundary_object()(pwh); + Construct_curves_2 construct_curves_func; + Cci_pair itr_pair = construct_curves_func(pgn2); + + std::list curves; + std::copy (itr_pair.first, itr_pair.second, + std::back_inserter(curves)); + + std::pair pair = traits.construct_holes_object()(pwh); + for (hoit = pair.first; hoit!=pair.second; ++hoit) + //for (hoit = pgn.holes_begin(); hoit != pgn.holes_end(); ++hoit) + { + itr_pair = construct_curves_func (*hoit); + std::copy (itr_pair.first, itr_pair.second, + std::back_inserter(curves)); + } + + // Perform the sweep and check for curve intersections on the interior. + //Traits_2 traits; moved to top, needed also for boundary. + Visitor visitor(false); + Sweep_line sweep_line (&traits, &visitor); + visitor.sweep (curves.begin(), curves.end()); + if (!visitor.is_valid()) + return false; + + Polygon_set_2 gps(traits); + //check for 2D intersections of holes (holes must be disjoint except for vertices) + Size num_of_holes=0; + for (hoit = pwh.holes_begin(); hoit != pwh.holes_end(); ++hoit) { + Polygon_2 hole(*hoit); + hole.reverse_orientation(); + /*gps.join() and gps.insert()requires that the polyon insrted is valid, and therfore hole + orientation must be reversed*/ + bool intersect = gps.do_intersect(hole); + if (intersect) + return false; + else { + /*to use gps.insert(hole) it is required that the set coponents and the new holes do not intersect. + because the sweep detects shared edges and the do_intersect query detects 2D intersections we can safely use + the insert(pwh) function whose performance is better than the join(pgn)*/ + Polygon_with_holes_2 empty_pwh(hole); + gps.insert(empty_pwh); + num_of_holes++; + } + } + /*not good - doesn't work if intersection at vertices is legal. + Size arr_num_of_holes = gps.number_of_polygons_with_holes(); + if (num_of_holes != arr_num_of_holes) + return false; + */ + //check for intersection of holes with the outer boundary + Hole_const_iterator fit; + /*outer boundary can be relatively simple. Execution of + do_intersect(hole, boundary) or difference(hole,boundary) relies on + implementation of General polygon set which has a precondition that requires + valid polygon or PWH to be inserted (not just a simple polygon). + This helper function is utilized after checking for the PWH closure, + relative simplicity and orientation. Therefore it is safe to assume the + outer boundary is valid PWH with no holes. We can't assume it is a valid + (simple) polygon. */ + Polygon_with_holes_2 boundary(pwh.outer_boundary(), fit, fit); + //unbounded outer boundaries contain all the holes and are OK + if (boundary.is_unbounded()) + return true; + /*do_intersect predicate will not suffice as hole can be completely outside + the outer boundary in an (extremely strange) case + The gps now contains all the holes. the difference between the boundary and a union of all + the holes should be the empty set. For performance reasons, we use a customized overlay traits and + perform an arrangement overlay instead of difference */ + ValidationOverlayTraits valOverlayTraits; + valOverlayTraits.setHoleOverlap(false); + Polygon_set_2 gps2(traits); + + Arrangement_2 boundary_arr = gps2.arrangement(); + gps2._insert(boundary,boundary_arr); + Arrangement_2 holes_arr = gps.arrangement(); + Arrangement_2 output_arr; + overlay(holes_arr, boundary_arr, output_arr, valOverlayTraits); + if (valOverlayTraits.getHoleOverlap()) + return false; + + /*old code that works less efficiently than the new overly traits + gps.validation_difference(boundary); + //if gps is not empty at least one hole intersected the boundary + if (!gps.is_empty()) + return (false); + */ + return (true); + } + + /*A valid polygon with holes is : + * 1 - Has empty or closed boundary and all the holes are closed + * 2 - The PWH is relatively simple polygon (holes are simple...) + * 3 - Has it's boundary oriented counterclockwise and the holes oriented clockwise + * 4 - All the segments (boundry and holes) do not cross or intersect in their relative interior + * 5 - The holes are on the interior of the boundary polygon if the boundary is not empty + */ + template + bool is_valid_polygon_with_holes(const typename Traits_2::Polygon_with_holes_2& pgn, Traits_2 traits) + { + bool closed = is_closed_polygon_with_holes(pgn,traits); + CGAL_warning_msg (closed, + "The polygon's boundary or one of it's holes is not closed."); + if (! closed) + return (false); + + bool relatively_simple = is_relatively_simple_polygon_with_holes(pgn,traits); + CGAL_warning_msg (relatively_simple, + "The polygon is not relatively simple."); + if (! relatively_simple) + return (false); + + bool no_cross = is_crossover_outer_boundary(pgn, traits); + CGAL_warning_msg (no_cross, + "The polygon has a crossover."); + if (!no_cross) + return (false); + + bool valid_orientation = has_valid_orientation_polygon_with_holes(pgn,traits); + CGAL_warning_msg (valid_orientation, + "The polygon has a wrong orientation."); + if (! valid_orientation) + return (false); + + bool holes_disjoint = are_holes_and_boundary_pairwise_disjoint(pgn,traits); + CGAL_warning_msg (holes_disjoint, + "Holes of the PWH intersect amongst themselves or with outer boundary"); + if (! holes_disjoint) + return false; + + return (true); + } + + template + bool is_valid_unknown_polygon(const typename Traits_2::Polygon_with_holes_2& pgn, + Traits_2 traits) + { + return is_valid_polygon_with_holes(pgn, traits); + } + + template + bool is_valid_unknown_polygon(const typename Traits_2::Polygon_2& pgn, + Traits_2 traits) + { + return is_valid_polygon(pgn, traits); + } + CGAL_END_NAMESPACE #endif + + diff --git a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_utils.h b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_utils.h index a41c0cdedd2..3d2f2efa98e 100644 --- a/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_utils.h +++ b/Boolean_set_operations_2/include/CGAL/Boolean_set_operations_2/Gps_utils.h @@ -269,7 +269,6 @@ template void General_polygon_set_2:: _insert(const Polygon_2& pgn, Arrangement_2 & arr) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); typedef Arr_accessor Arr_accessor; Arr_accessor accessor(arr); @@ -379,6 +378,11 @@ void General_polygon_set_2:: insert(PolygonIter p_begin, PolygonIter p_end) { typename std::iterator_traits::value_type pgn; + //check validity of all polygons + for( ; p_begin != p_end; ++p_begin) + { + CGAL_precondition(is_valid_unkown_polygon(*p_begin, *m_traits)); + } _insert(p_begin, p_end, pgn); } @@ -396,14 +400,14 @@ insert(PolygonIter p_begin, PolygonIter p_end, for( ; p_begin != p_end; ++p_begin) { - CGAL_precondition(m_traits->is_valid_2_object()(*p_begin)); + CGAL_precondition(is_valid_polygon(*p_begin, *m_traits)); _construct_curves(*p_begin, std::back_inserter(xcurve_list)); } bool is_unbounded = false; for( ; pwh_begin != pwh_end; ++pwh_begin) { - CGAL_precondition(m_traits->is_valid_2_object()(*pwh_begin)); + CGAL_precondition(is_valid_polygon_with_holes(*pwh_begin, *m_traits)); is_unbounded = (is_unbounded || m_traits->construct_is_unbounded_object()(*pwh_begin)); // is_unbounded = (is_unbounded || pwh_begin->is_unbounded()); _construct_curves(*pwh_begin, std::back_inserter(xcurve_list)); @@ -427,8 +431,7 @@ _insert(PolygonIter p_begin, PolygonIter p_end, Polygon_2 & /*pgn*/) { for(PolygonIter pitr = p_begin; pitr != p_end; ++pitr) { - CGAL_precondition(m_traits->is_valid_2_object()(*pitr)); - this->_insert(*pitr, *m_arr); + this->_insert(*pitr, *m_arr); } } @@ -445,8 +448,7 @@ _insert(PolygonIter p_begin, PolygonIter p_end, Polygon_with_holes_2 & /*pgn*/) bool is_unbounded = false; for( ; p_begin != p_end; ++p_begin) { - CGAL_precondition(m_traits->is_valid_2_object()(*p_begin)); -// is_unbounded = (is_unbounded || p_begin->is_unbounded()); + // is_unbounded = (is_unbounded || p_begin->is_unbounded()); is_unbounded = (is_unbounded || m_traits->construct_is_unbounded_object()(*p_begin)); _construct_curves(*p_begin, std::back_inserter(xcurve_list)); @@ -469,7 +471,8 @@ template void General_polygon_set_2:: _insert(const Polygon_with_holes_2 & pgn, Arrangement_2 & arr) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + //not needed gps.insert(PWH) has the precondition + // CGAL_precondition(is_valid_polygon_with_holes(pgn, *m_traits)); typedef std::list XCurveList; typedef Init_faces_visitor My_visitor; typedef Gps_bfs_scanner Arr_bfs_scanner; @@ -502,8 +505,7 @@ _construct_curves(const Polygon_2 & pgn, OutputIterator oi) template template -void -General_polygon_set_2:: +void General_polygon_set_2:: _construct_curves(const Polygon_with_holes_2 & pgn, OutputIterator oi) { //if (!pgn.is_unbounded()) diff --git a/Boolean_set_operations_2/include/CGAL/General_polygon_set_2.h b/Boolean_set_operations_2/include/CGAL/General_polygon_set_2.h index e071148ce8b..4ebd4875d70 100644 --- a/Boolean_set_operations_2/include/CGAL/General_polygon_set_2.h +++ b/Boolean_set_operations_2/include/CGAL/General_polygon_set_2.h @@ -148,7 +148,7 @@ public: m_traits_owner(true), m_arr(new Arrangement_2(m_traits)) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon(pgn, *m_traits)); _insert(pgn, *m_arr); } @@ -157,7 +157,7 @@ public: m_traits_owner(true), m_arr(new Arrangement_2(m_traits)) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn_with_holes)); + CGAL_precondition(is_valid_polygon_with_holes(pgn_with_holes,*m_traits)); _insert(pgn_with_holes, *m_arr); } @@ -197,18 +197,17 @@ public: // insert a simple polygon void insert(const Polygon_2& pgn) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon(pgn, *m_traits)); _insert(pgn, *m_arr); } // insert a polygon with holes void insert(const Polygon_with_holes_2& pgn_with_holes) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn_with_holes)); + CGAL_precondition(is_valid_polygon_with_holes(pgn_with_holes, *m_traits)); _insert(pgn_with_holes, *m_arr); } - - + // insert a range of polygons that can be either simple polygons // or polygons with holes // precondition: the polygons are disjoint and simple @@ -228,7 +227,7 @@ public: // test for intersection with a simple polygon bool do_intersect(const Polygon_2 &pgn) const { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon(pgn,*m_traits)); Self other(pgn); return (do_intersect(other)); } @@ -236,7 +235,7 @@ public: // test for intersection with a polygon with holes bool do_intersect(const Polygon_with_holes_2& pgn_with_holes) const { - CGAL_precondition(m_traits->is_valid_2_object()(pgn_with_holes)); + CGAL_precondition(is_valid_polygon_with_holes(pgn_with_holes, *m_traits)); Self other(pgn_with_holes); return (do_intersect(other)); } @@ -278,14 +277,14 @@ public: // intersection with a simple polygon void intersection(const Polygon_2& pgn) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon(pgn, *m_traits)); _intersection(pgn); } // intersection with a polygon with holes void intersection(const Polygon_with_holes_2& pgn) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon_with_holes(pgn, *m_traits)); _intersection(pgn); } @@ -305,14 +304,14 @@ public: // join with a simple polygon void join(const Polygon_2& pgn) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon(pgn, *m_traits)); _join(pgn); } // join with a polygon with holes void join(const Polygon_with_holes_2& pgn) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon_with_holes(pgn, *m_traits)); _join(pgn); } @@ -328,21 +327,20 @@ public: _join(*(gps1.m_arr), *(gps2.m_arr), *(this->m_arr)); } - // difference with a simple polygon void difference (const Polygon_2& pgn) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon(pgn, *m_traits)); _difference(pgn); } // difference with a polygon with holes void difference (const Polygon_with_holes_2& pgn) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon_with_holes(pgn, *m_traits)); _difference(pgn); } - + //difference with another General_polygon_set_2 object void difference (const Self& other) { @@ -359,14 +357,14 @@ public: // symmetric_difference with a simple polygon void symmetric_difference(const Polygon_2& pgn) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon(pgn, *m_traits)); _symmetric_difference(pgn); } // symmetric_difference with a polygon with holes void symmetric_difference(const Polygon_with_holes_2& pgn) { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon_with_holes(pgn, *m_traits)); _symmetric_difference(pgn); } @@ -490,14 +488,14 @@ public: Oriented_side oriented_side(const Polygon_2& pgn) const { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon(pgn, *m_traits)); Self other(pgn); return (oriented_side(other)); } Oriented_side oriented_side(const Polygon_with_holes_2& pgn) const { - CGAL_precondition(m_traits->is_valid_2_object()(pgn)); + CGAL_precondition(is_valid_polygon_with_holes(pgn, *m_traits)); Self other(pgn); return (oriented_side(other)); } @@ -701,7 +699,7 @@ public: for (InputIterator itr = begin; itr!=end; ++itr, ++i) { - CGAL_precondition(m_traits->is_valid_2_object()(*itr)); + CGAL_precondition(is_valid_polygon((*itr), *m_traits)); arr_vec[i].first = new Arrangement_2(m_traits); _insert(*itr, *(arr_vec[i].first)); } @@ -726,7 +724,7 @@ public: for (InputIterator itr = begin; itr!=end; ++itr, ++i) { - CGAL_precondition(m_traits->is_valid_2_object()(*itr)); + CGAL_precondition(is_valid_polygon_with_holes((*itr), *m_traits)); arr_vec[i].first = new Arrangement_2(m_traits); _insert(*itr, *(arr_vec[i].first)); } @@ -753,14 +751,14 @@ public: for (InputIterator1 itr1 = begin1; itr1!=end1; ++itr1, ++i) { - CGAL_precondition(m_traits->is_valid_2_object()(*itr1)); + CGAL_precondition(is_valid_unknown_polygon(*itr1, *m_traits)); arr_vec[i].first = new Arrangement_2(m_traits); _insert(*itr1, *(arr_vec[i].first)); } for (InputIterator2 itr2 = begin2; itr2!=end2; ++itr2, ++i) { - CGAL_precondition(m_traits->is_valid_2_object()(*itr2)); + CGAL_precondition(is_valid_unknown_polygon(*itr2,*m_traits)); arr_vec[i].first = new Arrangement_2(m_traits); _insert(*itr2, *(arr_vec[i].first)); } @@ -801,7 +799,7 @@ public: for (InputIterator itr = begin; itr!=end; ++itr, ++i) { - CGAL_precondition(m_traits->is_valid_2_object()(*itr)); + CGAL_precondition(is_valid_polygon(*itr,*m_traits)); arr_vec[i].first = new Arrangement_2(m_traits); _insert(*itr, *(arr_vec[i].first)); } @@ -826,7 +824,7 @@ public: for (InputIterator itr = begin; itr!=end; ++itr, ++i) { - CGAL_precondition(m_traits->is_valid_2_object()(*itr)); + CGAL_precondition(is_valid_polygon_with_holes(*itr,*m_traits)); arr_vec[i].first = new Arrangement_2(m_traits); _insert(*itr, *(arr_vec[i].first)); } @@ -853,14 +851,14 @@ public: for (InputIterator1 itr1 = begin1; itr1!=end1; ++itr1, ++i) { - CGAL_precondition(m_traits->is_valid_2_object()(*itr1)); + CGAL_precondition(is_valid_unknown_polygon(*itr1, *m_traits)); arr_vec[i].first = new Arrangement_2(m_traits); _insert(*itr1, *(arr_vec[i].first)); } for (InputIterator2 itr2 = begin2; itr2!=end2; ++itr2, ++i) { - CGAL_precondition(m_traits->is_valid_2_object()(*itr2)); + CGAL_precondition(is_valid_unknown_polygon(*itr2,*m_traits)); arr_vec[i].first = new Arrangement_2(m_traits); _insert(*itr2, *(arr_vec[i].first)); } diff --git a/Boolean_set_operations_2/include/CGAL/Gps_segment_traits_2.h b/Boolean_set_operations_2/include/CGAL/Gps_segment_traits_2.h index 2ea7365f014..bd0e96b8ad5 100644 --- a/Boolean_set_operations_2/include/CGAL/Gps_segment_traits_2.h +++ b/Boolean_set_operations_2/include/CGAL/Gps_segment_traits_2.h @@ -126,14 +126,14 @@ public: * An auxiliary functor used for validity checks. */ typedef Gps_traits_adaptor Traits_adaptor; - typedef CGAL::Is_valid_2 Is_valid_2; - + + /* typedef CGAL::Is_valid_2 Is_valid_2; Is_valid_2 is_valid_2_object() { Traits_adaptor tr_adp; return (Is_valid_2 (*this, tr_adp)); - } + }*/ //Added Functionality from GeneralPolygonWithHoles Concept to the traits. diff --git a/Boolean_set_operations_2/include/CGAL/Gps_traits_2.h b/Boolean_set_operations_2/include/CGAL/Gps_traits_2.h index bacf0f75a1c..ecb9cdb90e9 100644 --- a/Boolean_set_operations_2/include/CGAL/Gps_traits_2.h +++ b/Boolean_set_operations_2/include/CGAL/Gps_traits_2.h @@ -104,14 +104,15 @@ public: * An auxiliary functor used for validity checks. */ typedef Gps_traits_adaptor Traits_adaptor; - typedef CGAL::Is_valid_2 Is_valid_2; + /*typedef CGAL::Is_valid_2 Is_valid_2; Is_valid_2 is_valid_2_object() { Traits_adaptor tr_adp; - + return (Is_valid_2 (*this, tr_adp)); - } + }*/ + //Added Functionality from GeneralPolygonWithHoles Concept to the traits. /*A functor for constructing the outer boundary of a polygon with holes*/ diff --git a/Boolean_set_operations_2/include/CGAL/connect_holes.h b/Boolean_set_operations_2/include/CGAL/connect_holes.h index 6f320afc13d..1c70cac7081 100644 --- a/Boolean_set_operations_2/include/CGAL/connect_holes.h +++ b/Boolean_set_operations_2/include/CGAL/connect_holes.h @@ -15,6 +15,7 @@ // $Id$ // // Author(s) : Ron Wein +// Guy Zucker #ifndef CGAL_CONNECT_HOLES_H #define CGAL_CONNECT_HOLES_H diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/agg_op_test_suite_generator.cpp b/Boolean_set_operations_2/test/Boolean_set_operations_2/agg_op_test_suite_generator.cpp index 467f0b08b23..1c00689e53f 100644 --- a/Boolean_set_operations_2/test/Boolean_set_operations_2/agg_op_test_suite_generator.cpp +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/agg_op_test_suite_generator.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,7 @@ bool are_polygons_valid(Vec& vec) unsigned int i=0; for(; i < vec.size(); ++i) { - if(!tr.is_valid_2_object()(vec[i])) + if(!is_valid_unknown_polygon(vec[i], tr)) return false; } return true; diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/bop_test_suite_generator.cpp b/Boolean_set_operations_2/test/Boolean_set_operations_2/bop_test_suite_generator.cpp index eff0d41a1c6..5fa294f6df8 100644 --- a/Boolean_set_operations_2/test/Boolean_set_operations_2/bop_test_suite_generator.cpp +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/bop_test_suite_generator.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,7 @@ bool are_polygons_valid(const std::vector& vec) unsigned int i=0; for(; i < vec.size(); ++i) { - if(!tr.is_valid_2_object()(vec[i])) + if(!is_valid_polygon_with_holes(vec[i],tr)) return false; } return true; @@ -61,7 +62,7 @@ std::ostream& write_result_to_file(std::ostream& out, const T_P1& p1, const T_P2 { out << 1 << std::endl; out << res_pgn <> p1; out << p1; - if(! tr.is_valid_2_object()(p1)) + if(! is_valid_polygon(p1,tr)) { std::cout<<"warning: first input polygon is invalid!!!\n"; } @@ -197,7 +198,7 @@ int main(int argc, char **argv) { inp1 >> pwh1; out << pwh1; - if(! tr.is_valid_2_object()(pwh1)) + if(! is_valid_polygon_with_holes(pwh1,tr)) { std::cout<<"warning: first input polygon is invalid!!!\n"; } @@ -213,7 +214,7 @@ int main(int argc, char **argv) { inp2 >> p2; out << p2; - if(! tr.is_valid_2_object()(p2)) + if(!is_valid_polygon(p2,tr)) { std::cout<<"warning: second input polygon is invalid!!!\n"; } @@ -222,7 +223,7 @@ int main(int argc, char **argv) { inp2 >> pwh2; out << pwh2; - if(! tr.is_valid_2_object()(pwh2)) + if(! is_valid_polygon_with_holes(pwh2,tr)) { std::cout<<"warning: second input polygon is invalid!!!\n"; } diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test1.dat b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test1.dat new file mode 100644 index 00000000000..e295eb5b228 --- /dev/null +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test1.dat @@ -0,0 +1,16 @@ +4 +0 0 +8 0 +8 8 +0 8 + +1 + +6 +1 1 +2 2 +3 1 +4 2 +5 1 +3 1 + diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test2.dat b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test2.dat new file mode 100644 index 00000000000..c98651a9b76 --- /dev/null +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test2.dat @@ -0,0 +1,18 @@ +6 +0 0 +8 0 +8 8 +6 8 +2 8 +0 8 + +1 + +5 +1 6 +2 8 +4 12 +6 8 +7 6 + + diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test3.dat b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test3.dat new file mode 100644 index 00000000000..dfe50df2313 --- /dev/null +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test3.dat @@ -0,0 +1,19 @@ +4 +0 0 +8 0 +8 8 +0 8 + +2 + +4 +2 2 +2 6 +6 6 +6 2 + +4 +5 4 +5 5 +7 5 +7 4 diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test4.dat b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test4.dat new file mode 100644 index 00000000000..cf766a9b0cd --- /dev/null +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test4.dat @@ -0,0 +1,18 @@ +4 +0 0 +8 0 +8 8 +0 8 + +2 + +4 +1 1 +1 7 +7 7 +7 1 +4 +2 2 +2 6 +6 6 +6 2 diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test5.dat b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test5.dat new file mode 100644 index 00000000000..b0b9105f72d --- /dev/null +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test5.dat @@ -0,0 +1,18 @@ +4 +0 0 +8 0 +8 8 +0 8 + +2 + +4 +1 2 +1 6 +2 6 +2 2 +4 +2 2 +2 6 +6 6 +6 2 diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test6.dat b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test6.dat new file mode 100644 index 00000000000..ff7dde311a0 --- /dev/null +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test6.dat @@ -0,0 +1,15 @@ +6 +0 0 +2 0 +4 0 +8 0 +8 8 +0 8 + +1 + +4 +2 0 +2 6 +4 6 +4 0 diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test7.dat b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test7.dat new file mode 100644 index 00000000000..d76daef33b4 --- /dev/null +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test7.dat @@ -0,0 +1,12 @@ +6 +0 2 +4 2 +6 4 +6 0 +4 2 +2 4 + +0 + + + diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test8.dat b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test8.dat new file mode 100644 index 00000000000..886b6833a3b --- /dev/null +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test8.dat @@ -0,0 +1,12 @@ +6 +0 2 +4 2 +6 0 +6 4 +4 2 +2 4 + +0 + + + diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test9.dat b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test9.dat new file mode 100644 index 00000000000..926824e1d5a --- /dev/null +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/val_test9.dat @@ -0,0 +1,19 @@ +5 +0 0 +4 0 +8 0 +8 8 +0 8 + +2 + +4 +4 0 +2 2 +4 4 +4 2 + +3 +4 4 +2 6 +6 6 diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/validation_test_output.txt b/Boolean_set_operations_2/test/Boolean_set_operations_2/data/validation/validation_test_output.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Boolean_set_operations_2/test/Boolean_set_operations_2/test_polygon_validation.cpp b/Boolean_set_operations_2/test/Boolean_set_operations_2/test_polygon_validation.cpp new file mode 100644 index 00000000000..45143ede658 --- /dev/null +++ b/Boolean_set_operations_2/test/Boolean_set_operations_2/test_polygon_validation.cpp @@ -0,0 +1,133 @@ +/*test file for polygon validation. Intended for testing the global functions defined at Gps_polygon_validation.h*/ + + +#ifndef CGAL_BSO_RATIONAL_NT_H +#define CGAL_BSO_RATIONAL_NT_H + +#include + +#ifdef CGAL_USE_GMP + // GMP is installed. Use the GMP rational number-type. + #include + typedef CGAL::Gmpq Number_type; +#else + // GMP is not installed. Use CGAL's exact rational number-type. + #include + #include + typedef CGAL::Quotient Number_type; +#endif +#endif + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef CGAL::Cartesian Kernel; +typedef Kernel::Point_2 Point_2; +typedef Kernel::Circle_2 Circle_2; +typedef CGAL::Gps_segment_traits_2 Traits_2; +typedef CGAL::General_polygon_set_2 Polygon_set_2; +typedef Traits_2::Polygon_2 Polygon_2; +typedef Traits_2::Polygon_with_holes_2 Polygon_with_holes_2; +typedef CGAL::Arr_segment_traits_2 Base; +typedef CGAL::Gps_traits_adaptor Traits_adaptor; +typedef std::list Pwh_list_2; + +/*test files: + +1. val_test1.dat - invalid polygon with holes. The hole is relatively simple instead of strictly simple. +2. val_test2.dat - invalid polygon with holes. The hole overlaps the outer boundary (the intersection results in a polygon). +3. val_test3.dat - invalid polygon with holes. Two holes intersect (the intersection results in a polygon). +4. val_test4.dat - invalid polygon with holes. Two holes intersect (one contains the other). +5. val_test5.dat - invalid polygon with holes. Two holes share an edge. (non regularized intersection +results in an edge). +6. val_test6.dat - invalid polygon with holes. A hole and the outer boundary share an edge. (non regularized intersection +results in an edge). +7. val_test7.dat - invalid polygon with holes. The outer boundary is not relatively simple because a "crossover" occurs + at an intersection +8. val_test8.dat - valid polygon with holes. Outer boundary is relatively simple. +9. val_test9.dat - valid polygon with holes. Outer Boundary and holes are pairwise disjoint except on vertices +*/ + + +/*test an input file. isValid indicates the input polygon is valid. ErrorMsg is displayed if the validation result does't equal isValid */ +bool testValidationForFile(const char* infilename, std::ofstream& outfile , bool isValid) { + std::ifstream input_file (infilename); + + if (! input_file.is_open()) { + std::cerr << "Failed to open the " << infilename <> outerP; + input_file >> num_holes; + + std::vector holes (num_holes); + unsigned int k; + + for (k = 0; k < num_holes; k++) + input_file >> holes[k]; + Polygon_with_holes_2 P (outerP, holes.begin(), holes.end()); + Traits_2 tr; + bool testValid = CGAL::is_valid_polygon_with_holes(P,tr); + bool res = true; + if (testValid != isValid) { + res=false; + outfile<< "Error validating " << infilename <> si; + std::string filename = testfilePrefix + si + testfileSuffix; + const char *cfilename = filename.c_str(); + bool isValidPgn=false; + if (i>7) + isValidPgn=true; + bool res = testValidationForFile(cfilename, output_file, isValidPgn); + if (!res) { + std::cout << "test " << i << " failed" << std::endl; + result=1; + } + else { + std::cout <<"test " << i << " succeeded" << std::endl; + } + } + if (result == 0) + std::cout <<"ALL TESTS SUCCEEDED!" << std::endl; + else + std::cout <<"SOME TESTS FAILED" << std::endl; + return (0); +} +