From 5e7e4ee8dd9f4a7296f535f1ed5e44cd734f2f89 Mon Sep 17 00:00:00 2001 From: Efi Fogel Date: Fri, 23 Aug 2013 01:24:45 +0300 Subject: [PATCH] Added support for the computation of Minkowski sum of polygons with holes --- .../examples/Minkowski_sum_2/holes.dat | 6 + .../examples/Minkowski_sum_2/sum_of_holes.cpp | 42 +++ .../Minkowski_sum_2/Minkowski_sum_decomp_2.h | 68 ++++- .../CGAL/Polygon_vertical_decomposition_2.h | 245 ++++++++++++++++++ .../include/CGAL/minkowski_sum_2.h | 26 +- 5 files changed, 372 insertions(+), 15 deletions(-) create mode 100644 Minkowski_sum_2/examples/Minkowski_sum_2/holes.dat create mode 100644 Minkowski_sum_2/examples/Minkowski_sum_2/sum_of_holes.cpp create mode 100644 Minkowski_sum_2/include/CGAL/Polygon_vertical_decomposition_2.h diff --git a/Minkowski_sum_2/examples/Minkowski_sum_2/holes.dat b/Minkowski_sum_2/examples/Minkowski_sum_2/holes.dat new file mode 100644 index 00000000000..dbc084fc214 --- /dev/null +++ b/Minkowski_sum_2/examples/Minkowski_sum_2/holes.dat @@ -0,0 +1,6 @@ +4 0 0 3 0 3 3 0 3 +1 +4 1 1 1 2 2 2 2 1 +4 0 0 3 0 3 3 0 3 +1 +4 1 1 1 2 2 2 2 1 diff --git a/Minkowski_sum_2/examples/Minkowski_sum_2/sum_of_holes.cpp b/Minkowski_sum_2/examples/Minkowski_sum_2/sum_of_holes.cpp new file mode 100644 index 00000000000..232c854ba1b --- /dev/null +++ b/Minkowski_sum_2/examples/Minkowski_sum_2/sum_of_holes.cpp @@ -0,0 +1,42 @@ +//! \file examples/Minkowski_sum_2/sum_by_decomposition.cpp +// Computing the Minkowski sum of two non-convex polygons read from a file +// using the small-side angle-bisector decomposition strategy. + +#include +#include +#include +#include +#include + +#include "print_utils.h" + +typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; + +typedef Kernel::Point_2 Point_2; +typedef CGAL::Polygon_2 Polygon_2; +typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; + +int main() +{ + // Open the input file. + std::ifstream in_file("holes.dat"); + + if (! in_file.is_open()) { + std::cerr << "Failed to open the input file." << std::endl; + return (1); + } + + // Read the two polygons from the file and compute their Minkowski sum. + Polygon_with_holes_2 P, Q; + + in_file >> P >> Q; + in_file.close(); + + // Compute the Minkowski sum using the decomposition approach. + CGAL::Polygon_vertical_decomposition_2 vertical_decomp; + Polygon_with_holes_2 sum = minkowski_sum_2(P, Q, vertical_decomp); + std::cout << "P (+) Q = "; + print_polygon_with_holes(sum); + + return 0; +} diff --git a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/Minkowski_sum_decomp_2.h b/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/Minkowski_sum_decomp_2.h index c78a4e63419..3accdf2c9a9 100644 --- a/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/Minkowski_sum_decomp_2.h +++ b/Minkowski_sum_2/include/CGAL/Minkowski_sum_2/Minkowski_sum_decomp_2.h @@ -40,7 +40,7 @@ template class Minkowski_sum_by_decomposition_2 { public: - + typedef DecompStrategy_ Decomposition_strategy; typedef Container_ Container; typedef typename Decomposition_strategy::Polygon_2 Polygon_2; @@ -52,7 +52,7 @@ private: typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Vector_2 Vector_2; typedef typename Kernel::Direction_2 Direction_2; - + // Kernel functors: typedef typename Kernel::Equal_2 Equal_2; typedef typename Kernel::Compare_angle_with_x_axis_2 Compare_angle_2; @@ -96,7 +96,7 @@ public: f_equal = ker.equal_2_object(); f_compare_angle = ker.compare_angle_with_x_axis_2_object(); - f_add = ker.construct_translated_point_2_object(); + f_add = ker.construct_translated_point_2_object(); f_vector = ker.construct_vector_2_object(); f_direction = ker.construct_direction_2_object(); f_orientation = ker.orientation_2_object(); @@ -142,18 +142,60 @@ public: _compute_sum_of_convex (*curr1, *curr2, sub_sum); sub_sum_polygons.push_back(sub_sum); - + } } - + General_polygon_set_2 gps; - + gps.join(sub_sum_polygons.begin(),sub_sum_polygons.end()); - + Polygon_with_holes_list sum; gps.polygons_with_holes(std::back_inserter(sum)); - + + return (*(sum.begin())); + } + + /*! + * Compute the Minkowski sum of two polygon-with-holes. + * \param pgn1 The first polygon. + * \param pgn2 The second polygon. + * \pre Both input polygons are simple. + * \return The resulting polygon with holes, representing the sum. + */ + Polygon_with_holes_2 + operator()(const Polygon_with_holes_2& pgn1, + const Polygon_with_holes_2& pgn2) const + { + // Decompose both input polygons to convex sub-polygons. + Decomposition_strategy decomp_strat; + Polygons_list sub_pgns1; + Polygons_list sub_pgns2; + Polygons_list sub_sum_polygons; + + decomp_strat(pgn1, std::back_inserter(sub_pgns1)); + decomp_strat(pgn2, std::back_inserter(sub_pgns2)); + + // Compute the sub-sums of all pairs of sub-polygons. + Polygons_iterator end1 = sub_pgns1.end(); + Polygons_iterator end2 = sub_pgns2.end(); + Polygons_iterator curr1, curr2; + + for (curr1 = sub_pgns1.begin(); curr1 != end1; ++curr1) { + for (curr2 = sub_pgns2.begin(); curr2 != end2; ++curr2) { + // Compute the sum of the current pair of convex sub-polygons. + Polygon_2 sub_sum; + _compute_sum_of_convex(*curr1, *curr2, sub_sum); + sub_sum_polygons.push_back(sub_sum); + } + } + + General_polygon_set_2 gps; + gps.join(sub_sum_polygons.begin(), sub_sum_polygons.end()); + Polygon_with_holes_list sum; + gps.polygons_with_holes(std::back_inserter(sum)); + CGAL_assertion(sum.size() == 1); return (*(sum.begin())); } @@ -189,7 +231,7 @@ private: // Find the bottom-left vertex in both polygons. Vertex_circulator first2, curr2, next2; Vertex_circulator bottom_left2; - + bottom_left2 = curr2 = first2 = pgn2.vertices_circulator(); ++curr2; while (curr2 != first2) @@ -208,7 +250,7 @@ private: ++next1; next2 = curr2 = bottom_left2; ++next2; - + // Compute the Minkowski sum. Point_2 first_pt; Point_2 curr_pt; @@ -241,7 +283,7 @@ private: } // Compare the angles the current edges form with the x-axis. - res = f_compare_angle (f_direction (f_vector (*curr1, *next1)), + res = f_compare_angle (f_direction (f_vector (*curr1, *next1)), f_direction (f_vector (*curr2, *next2))); // Proceed to the next vertex according to the result. @@ -265,8 +307,8 @@ private: curr1 = next1; ++next1; moved_on1 = true; - } - + } + if (inc2) { curr2 = next2; diff --git a/Minkowski_sum_2/include/CGAL/Polygon_vertical_decomposition_2.h b/Minkowski_sum_2/include/CGAL/Polygon_vertical_decomposition_2.h new file mode 100644 index 00000000000..7ac820d1ea9 --- /dev/null +++ b/Minkowski_sum_2/include/CGAL/Polygon_vertical_decomposition_2.h @@ -0,0 +1,245 @@ +// Copyright (c) 2006 Tel-Aviv University (Israel). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// 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) : Ron Wein +// (based on an old version by Eyal Flato) + +#ifndef CGAL_SMALL_SIDE_ANGLE_BISECTOR_DECOMPOSITION_2_H +#define CGAL_SMALL_SIDE_ANGLE_BISECTOR_DECOMPOSITION_2_H + +#include +#include +#include + +#include +#include + +namespace CGAL { + +/*! + * \class + * Vertical decomposition strategy. + */ +template > +class Polygon_vertical_decomposition_2 { +public: + typedef Kernel_ Kernel; + typedef Container_ Container; + + typedef CGAL::Polygon_2 Polygon_2; + typedef CGAL::Polygon_with_holes_2 Polygon_with_holes_2; + + typedef CGAL::Arr_segment_traits_2 Arr_segment_traits; + typedef CGAL::Gps_segment_traits_2 + Traits_2; + typedef CGAL::General_polygon_set_2 General_polygon_set_2; + typedef typename General_polygon_set_2::Arrangement_2 Arrangement_2; + + typedef typename Arrangement_2::Halfedge_const_iterator + Halfedge_const_iterator; + typedef typename Arrangement_2::Face_const_iterator Face_const_iterator; + + typedef typename Arrangement_2::Vertex_const_handle Vertex_const_handle; + typedef typename Arrangement_2::Halfedge_const_handle Halfedge_const_handle; + typedef typename Arrangement_2::Face_const_handle Face_const_handle; + + typedef typename Arrangement_2::Vertex_handle Vertex_handle; + typedef typename Arrangement_2::Halfedge_handle Halfedge_handle; + typedef typename Arrangement_2::Face_handle Face_handle; + + typedef std::pair > + Vert_decomp_entry; + typedef std::list Vert_decomp_list; + + typedef typename Kernel::Point_2 Point_2; + + typedef typename Arrangement_2::Outer_ccb_const_iterator + Outer_ccb_const_iterator; + +private: + + typedef typename Arrangement_2::X_monotone_curve_2 Segment_2; + typedef typename Kernel::Line_2 Line_2; + + typedef typename Polygon_2::Vertex_circulator Vertex_circulator; + + // An arrangement observer, used to receive notifications of face splits and + // face mergers. + class My_observer : public CGAL::Arr_observer { + public: + My_observer (Arrangement_2& arr) : + CGAL::Arr_observer(arr) + {} + + virtual void after_split_face(Face_handle f, Face_handle new_f, + bool /* is_hole */) + { if (f->contained()) new_f->set_contained(true); } + }; + + // Kernel functors: + typedef typename Kernel::Compare_x_2 Compare_x_2; + typedef typename Kernel::Intersect_2 Intersect_2; + typedef typename Kernel::Equal_2 Equal_2; + + // Data members: + Kernel* m_kernel; + bool m_own_kernel; // inidicates whether the kernel should be freed up. + + Compare_x_2 f_cmp_x; + Intersect_2 f_intersect; + Equal_2 f_equal; + +public: + + /*! Default constructor. */ + Polygon_vertical_decomposition_2() : + m_kernel(new Kernel), + m_own_kernel(true) + { + // Obtain kernel functors. + f_cmp_x = m_kernel->compare_x_2_object(); + f_intersect = m_kernel->intersect_2_object(); + f_equal = m_kernel->equal_2_object(); + } + + // Destructor + ~Polygon_vertical_decomposition_2() + { + if (m_own_kernel && m_kernel) { + delete m_kernel; + m_kernel = NULL; + m_own_kernel = false; + } + } + + /*! + * Decompose a polygon-with-holes into convex sub-polygons. + * \param pgn The input polygon. + * \param oi An output iterator of convex polygons. + * \return A past-the-end iterator for the sub-polygons. + */ + template + OutputIterator operator()(const Polygon_with_holes_2& pgn, + OutputIterator oi) const + { + General_polygon_set_2 gps; + gps.insert(pgn); + Arrangement_2& arr = gps.arrangement(); + My_observer obs(arr); + vertical_decomposition(arr); + Face_const_iterator fi; + for (fi = arr.faces_begin(); fi != arr.faces_end(); ++fi) { + if (! fi->contained()) continue; + CGAL_assertion(fi->number_of_outer_ccbs() == 1); + Outer_ccb_const_iterator oci = fi->outer_ccbs_begin(); + Halfedge_const_iterator first = *oci; + Halfedge_const_iterator curr = first; + Polygon_2 pgn; + do { + pgn.push_back(curr->target()->point()); + curr = curr->next(); + } while (curr != first); + *oi++ = pgn; + } + return oi; + } + +private: + // Add a vertical segment from the given vertex to some other arrangement + // feature. + Halfedge_const_handle + add_vertical_segment(Arrangement_2& arr, Vertex_handle v, CGAL::Object obj) + const + { + Segment_2 seg; + Vertex_const_handle vh; + Halfedge_const_handle hh; + Face_const_handle fh; + Vertex_handle v2; + + if (CGAL::assign(vh, obj)) { + // The given feature is a vertex. + seg = Segment_2(v->point(), vh->point()); + v2 = arr.non_const_handle(vh); + } + else if (CGAL::assign(hh, obj)) { + // The given feature is a halfedge. We ignore fictitious halfedges. + if (hh->is_fictitious()) + return Halfedge_const_handle(); + + // Check whether v lies in the interior of the x-range of the edge (in + // which case this edge should be split). + if (f_cmp_x(v->point(), hh->target()->point()) == CGAL::EQUAL) { + // In case the target of the edge already has the same x-coordinate as + // the vertex v, just connect these two vertices. + seg = Segment_2(v->point(), hh->target()->point()); + v2 = arr.non_const_handle(hh->target()); + } + else { + // Compute the vertical projection of v onto the segment associated + // with the halfedge. Split the edge and connect v with the split point. + Line_2 supp_line(hh->source()->point(), hh->target()->point()); + Line_2 vert_line(v->point(), + Point_2(v->point().x(), v->point().y() + 1)); + Point_2 point; + CGAL::assign(point, f_intersect(supp_line, vert_line)); + seg = Segment_2(v->point(), point); + arr.split_edge(arr.non_const_handle(hh), + Segment_2(hh->source()->point(), point), + Segment_2(point, hh->target()->point())); + v2 = arr.non_const_handle(hh->target()); + } + } + // Ignore faces and empty objects. + else return Halfedge_const_handle(); + + // Add the vertical segment to the arrangement using its two end vertices. + return arr.insert_at_vertices(seg, v, v2); + } + + // Construct the vertical decomposition of the given arrangement. + void vertical_decomposition(Arrangement_2& arr) const + { + // For each vertex in the arrangment, locate the feature that lies + // directly below it and the feature that lies directly above it. + Vert_decomp_list vd_list; + CGAL::decompose(arr, std::back_inserter(vd_list)); + + // Go over the vertices (given in ascending lexicographical xy-order), + // and add segements to the feautres below and above it. + typename Vert_decomp_list::iterator it, prev = vd_list.end(); + for (it = vd_list.begin(); it != vd_list.end(); ++it) { + // If the feature above the previous vertex is not the current vertex, + // add a vertical segment to the feature below the vertex. + Vertex_const_handle v; + if ((prev == vd_list.end()) || + !CGAL::assign(v, prev->second.second) || + !f_equal(v->point(), it->first->point())) + add_vertical_segment(arr, arr.non_const_handle(it->first), + it->second.first); + // Add a vertical segment to the feature above the vertex. + add_vertical_segment(arr, arr.non_const_handle(it->first), + it->second.second); + prev = it; + } + } +}; + +} //namespace CGAL + +#endif diff --git a/Minkowski_sum_2/include/CGAL/minkowski_sum_2.h b/Minkowski_sum_2/include/CGAL/minkowski_sum_2.h index e781a6552a2..15ce8285725 100644 --- a/Minkowski_sum_2/include/CGAL/minkowski_sum_2.h +++ b/Minkowski_sum_2/include/CGAL/minkowski_sum_2.h @@ -80,14 +80,36 @@ minkowski_sum_2 (const Polygon_2& pgn1, Minkowski_sum_by_decomposition_2 mink_sum; typedef Polygon_with_holes_2 Polygon_with_holes_2; - + Polygon_with_holes_2 sum; sum = mink_sum (pgn1, pgn2); - + return (sum); } +/*! + * Compute the Minkowski sum of two polygon-with-holes by decomposing each + * polygon to convex sub-polygons and computing the union of the pairwise + * Minkowski sums of the sub-polygons. + * The result is also represented as a polygon with holes. + * \param pgn1 The first polygon. + * \param pgn2 The second polygon. + * \param decomp A functor for decomposing polygons. + * \param sum Output: The resulting polygon with holes, representing the sum. + */ +template +Polygon_with_holes_2 +minkowski_sum_2(const Polygon_with_holes_2& pgn1, + const Polygon_with_holes_2& pgn2, + const DecompositionStrategy&) +{ + Minkowski_sum_by_decomposition_2 mink_sum; + typedef Polygon_with_holes_2 Polygon_with_holes_2; + Polygon_with_holes_2 sum = mink_sum(pgn1, pgn2); + return sum; +} + } //namespace CGAL #endif