You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
267 lines
12 KiB
267 lines
12 KiB
/* |
|
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 T> |
|
class ScanLineToRects { |
|
public: |
|
typedef T rectangle_type; |
|
typedef typename rectangle_traits<T>::coordinate_type coordinate_type; |
|
typedef rectangle_data<coordinate_type> scan_rect_type; |
|
private: |
|
|
|
typedef std::set<scan_rect_type, less_rectangle_concept<scan_rect_type, scan_rect_type> > ScanData; |
|
ScanData scanData_; |
|
bool haveCurrentRect_; |
|
scan_rect_type currentRect_; |
|
orientation_2d orient_; |
|
typename rectangle_traits<T>::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<coordinate_type>::max)(); |
|
} |
|
|
|
template <typename CT> |
|
inline ScanLineToRects& processEdge(CT& rectangles, const interval_data<coordinate_type>& edge); |
|
|
|
inline ScanLineToRects& nextMajorCoordinate(coordinate_type currentCoordinate) { |
|
if(haveCurrentRect_) { |
|
scanData_.insert(scanData_.end(), currentRect_); |
|
haveCurrentRect_ = false; |
|
} |
|
currentCoordinate_ = currentCoordinate; |
|
return *this; |
|
} |
|
|
|
}; |
|
|
|
template <class CT, class ST, class rectangle_type, typename interval_type, typename coordinate_type> 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<coordinate_type>(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<coordinate_type>(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<coordinate_type>(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<coordinate_type>(currentCoordinate, |
|
currentCoordinate)); |
|
currentRect.set(orient, interval_data<coordinate_type>((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<coordinate_type>(currentCoordinate, |
|
currentCoordinate)); |
|
currentRect.set(orient, interval_data<coordinate_type>((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<coordinate_type>(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<coordinate_type>(currentCoordinate, |
|
currentCoordinate)); |
|
tmpRect.set(orient, edge); |
|
scanData.insert(tmpRect); |
|
return rectangles; |
|
} |
|
return rectangles; |
|
|
|
} |
|
|
|
template <class T> |
|
template <class CT> |
|
inline |
|
ScanLineToRects<T>& ScanLineToRects<T>::processEdge(CT& rectangles, const interval_data<coordinate_type>& edge) |
|
{ |
|
processEdge_(rectangles, scanData_, edge, haveCurrentRect_, currentRect_, currentCoordinate_, orient_); |
|
return *this; |
|
} |
|
|
|
|
|
} //namespace rectangle_formation |
|
|
|
template <typename T, typename T2> |
|
struct get_coordinate_type_for_rectangles { |
|
typedef typename polygon_traits<T>::coordinate_type type; |
|
}; |
|
template <typename T> |
|
struct get_coordinate_type_for_rectangles<T, rectangle_concept> { |
|
typedef typename rectangle_traits<T>::coordinate_type type; |
|
}; |
|
|
|
template <typename output_container, typename iterator_type, typename rectangle_concept> |
|
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<rectangle_type, typename geometry_concept<rectangle_type>::type>::type Unit; |
|
rectangle_data<Unit> model; |
|
Unit prevPos = (std::numeric_limits<Unit>::max)(); |
|
rectangle_formation::ScanLineToRects<rectangle_data<Unit> > 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<Unit>(lowy, highy)); |
|
if(abs((*itr).second.second) > 1) itr = tmp_itr; //next edge begins from this vertex |
|
} |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
|