blob: 0cf941bb074f4facf741406df9afbcbc82eeebc3 [file] [log] [blame]
/*
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