/* Copyright 2008 Intel Corporation Use, modification and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt). */ #ifndef BOOST_POLYGON_RECTANGLE_FORMATION_HPP #define BOOST_POLYGON_RECTANGLE_FORMATION_HPP namespace boost { namespace polygon{ namespace rectangle_formation { template class ScanLineToRects { public: typedef T rectangle_type; typedef typename rectangle_traits::coordinate_type coordinate_type; typedef rectangle_data scan_rect_type; private: typedef std::set > ScanData; ScanData scanData_; bool haveCurrentRect_; scan_rect_type currentRect_; orientation_2d orient_; typename rectangle_traits::coordinate_type currentCoordinate_; public: inline ScanLineToRects() : scanData_(), haveCurrentRect_(), currentRect_(), orient_(), currentCoordinate_() {} inline ScanLineToRects(orientation_2d orient, rectangle_type model) : scanData_(orientation_2d(orient.to_int() ? VERTICAL : HORIZONTAL)), haveCurrentRect_(false), currentRect_(), orient_(orient), currentCoordinate_() { assign(currentRect_, model); currentCoordinate_ = (std::numeric_limits::max)(); } template inline ScanLineToRects& processEdge(CT& rectangles, const interval_data& edge); inline ScanLineToRects& nextMajorCoordinate(coordinate_type currentCoordinate) { if(haveCurrentRect_) { scanData_.insert(scanData_.end(), currentRect_); haveCurrentRect_ = false; } currentCoordinate_ = currentCoordinate; return *this; } }; template inline CT& processEdge_(CT& rectangles, ST& scanData, const interval_type& edge, bool& haveCurrentRect, rectangle_type& currentRect, coordinate_type currentCoordinate, orientation_2d orient) { typedef typename CT::value_type result_type; bool edgeProcessed = false; if(!scanData.empty()) { //process all rectangles in the scanData that touch the edge typename ST::iterator dataIter = scanData.lower_bound(rectangle_type(edge, edge)); //decrement beginIter until its low is less than edge's low while((dataIter == scanData.end() || (*dataIter).get(orient).get(LOW) > edge.get(LOW)) && dataIter != scanData.begin()) { --dataIter; } //process each rectangle until the low end of the rectangle //is greater than the high end of the edge while(dataIter != scanData.end() && (*dataIter).get(orient).get(LOW) <= edge.get(HIGH)) { const rectangle_type& rect = *dataIter; //if the rectangle data intersects the edge at all if(rect.get(orient).get(HIGH) >= edge.get(LOW)) { if(contains(rect.get(orient), edge, true)) { //this is a closing edge //we need to write out the intersecting rectangle and //insert between 0 and 2 rectangles into the scanData //write out rectangle rectangle_type tmpRect = rect; if(rect.get(orient.get_perpendicular()).get(LOW) < currentCoordinate) { //set the high coordinate perpedicular to slicing orientation //to the current coordinate of the scan event tmpRect.set(orient.get_perpendicular().get_direction(HIGH), currentCoordinate); result_type result; assign(result, tmpRect); rectangles.insert(rectangles.end(), result); } //erase the rectangle from the scan data typename ST::iterator nextIter = dataIter; ++nextIter; scanData.erase(dataIter); if(tmpRect.get(orient).get(LOW) < edge.get(LOW)) { //insert a rectangle for the overhang of the bottom //of the rectangle back into scan data rectangle_type lowRect(tmpRect); lowRect.set(orient.get_perpendicular(), interval_data(currentCoordinate, currentCoordinate)); lowRect.set(orient.get_direction(HIGH), edge.get(LOW)); scanData.insert(nextIter, lowRect); } if(tmpRect.get(orient).get(HIGH) > edge.get(HIGH)) { //insert a rectangle for the overhang of the top //of the rectangle back into scan data rectangle_type highRect(tmpRect); highRect.set(orient.get_perpendicular(), interval_data(currentCoordinate, currentCoordinate)); highRect.set(orient.get_direction(LOW), edge.get(HIGH)); scanData.insert(nextIter, highRect); } //we are done with this edge edgeProcessed = true; break; } else { //it must be an opening edge //assert that rect does not overlap the edge but only touches //write out rectangle rectangle_type tmpRect = rect; //set the high coordinate perpedicular to slicing orientation //to the current coordinate of the scan event if(tmpRect.get(orient.get_perpendicular().get_direction(LOW)) < currentCoordinate) { tmpRect.set(orient.get_perpendicular().get_direction(HIGH), currentCoordinate); result_type result; assign(result, tmpRect); rectangles.insert(rectangles.end(), result); } //erase the rectangle from the scan data typename ST::iterator nextIter = dataIter; ++nextIter; scanData.erase(dataIter); dataIter = nextIter; if(haveCurrentRect) { if(currentRect.get(orient).get(HIGH) >= edge.get(LOW)){ if(!edgeProcessed && currentRect.get(orient.get_direction(HIGH)) > edge.get(LOW)){ rectangle_type tmpRect2(currentRect); tmpRect2.set(orient.get_direction(HIGH), edge.get(LOW)); scanData.insert(nextIter, tmpRect2); if(currentRect.get(orient.get_direction(HIGH)) > edge.get(HIGH)) { currentRect.set(orient, interval_data(edge.get(HIGH), currentRect.get(orient.get_direction(HIGH)))); } else { haveCurrentRect = false; } } else { //extend the top of current rect currentRect.set(orient.get_direction(HIGH), (std::max)(edge.get(HIGH), tmpRect.get(orient.get_direction(HIGH)))); } } else { //insert current rect into the scanData scanData.insert(nextIter, currentRect); //create a new current rect currentRect.set(orient.get_perpendicular(), interval_data(currentCoordinate, currentCoordinate)); currentRect.set(orient, interval_data((std::min)(tmpRect.get(orient).get(LOW), edge.get(LOW)), (std::max)(tmpRect.get(orient).get(HIGH), edge.get(HIGH)))); } } else { haveCurrentRect = true; currentRect.set(orient.get_perpendicular(), interval_data(currentCoordinate, currentCoordinate)); currentRect.set(orient, interval_data((std::min)(tmpRect.get(orient).get(LOW), edge.get(LOW)), (std::max)(tmpRect.get(orient).get(HIGH), edge.get(HIGH)))); } //skip to nextIter position edgeProcessed = true; continue; } //edgeProcessed = true; } ++dataIter; } //end while edge intersects rectangle data } if(!edgeProcessed) { if(haveCurrentRect) { if(currentRect.get(orient.get_perpendicular().get_direction(HIGH)) == currentCoordinate && currentRect.get(orient.get_direction(HIGH)) >= edge.get(LOW)) { if(currentRect.get(orient.get_direction(HIGH)) > edge.get(LOW)){ rectangle_type tmpRect(currentRect); tmpRect.set(orient.get_direction(HIGH), edge.get(LOW)); scanData.insert(scanData.end(), tmpRect); if(currentRect.get(orient.get_direction(HIGH)) > edge.get(HIGH)) { currentRect.set(orient, interval_data(edge.get(HIGH), currentRect.get(orient.get_direction(HIGH)))); return rectangles; } else { haveCurrentRect = false; return rectangles; } } //extend current rect currentRect.set(orient.get_direction(HIGH), edge.get(HIGH)); return rectangles; } scanData.insert(scanData.end(), currentRect); haveCurrentRect = false; } rectangle_type tmpRect(currentRect); tmpRect.set(orient.get_perpendicular(), interval_data(currentCoordinate, currentCoordinate)); tmpRect.set(orient, edge); scanData.insert(tmpRect); return rectangles; } return rectangles; } template template inline ScanLineToRects& ScanLineToRects::processEdge(CT& rectangles, const interval_data& edge) { processEdge_(rectangles, scanData_, edge, haveCurrentRect_, currentRect_, currentCoordinate_, orient_); return *this; } } //namespace rectangle_formation template struct get_coordinate_type_for_rectangles { typedef typename polygon_traits::coordinate_type type; }; template struct get_coordinate_type_for_rectangles { typedef typename rectangle_traits::coordinate_type type; }; template void form_rectangles(output_container& output, iterator_type begin, iterator_type end, orientation_2d orient, rectangle_concept ) { typedef typename output_container::value_type rectangle_type; typedef typename get_coordinate_type_for_rectangles::type>::type Unit; rectangle_data model; Unit prevPos = (std::numeric_limits::max)(); rectangle_formation::ScanLineToRects > scanlineToRects(orient, model); for(iterator_type itr = begin; itr != end; ++ itr) { Unit pos = (*itr).first; if(pos != prevPos) { scanlineToRects.nextMajorCoordinate(pos); prevPos = pos; } Unit lowy = (*itr).second.first; iterator_type tmp_itr = itr; ++itr; Unit highy = (*itr).second.first; scanlineToRects.processEdge(output, interval_data(lowy, highy)); if(abs((*itr).second.second) > 1) itr = tmp_itr; //next edge begins from this vertex } } } } #endif