/* | |
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_BOOLEAN_OP_HPP | |
#define BOOST_POLYGON_BOOLEAN_OP_HPP | |
namespace boost { namespace polygon{ | |
namespace boolean_op { | |
//BooleanOp is the generic boolean operation scanline algorithm that provides | |
//all the simple boolean set operations on manhattan data. By templatizing | |
//the intersection count of the input and algorithm internals it is extensible | |
//to multi-layer scans, properties and other advanced scanline operations above | |
//and beyond simple booleans. | |
//T must cast to int | |
template <class T, typename Unit> | |
class BooleanOp { | |
public: | |
typedef std::map<Unit, T> ScanData; | |
typedef std::pair<Unit, T> ElementType; | |
protected: | |
ScanData scanData_; | |
typename ScanData::iterator nextItr_; | |
T nullT_; | |
public: | |
inline BooleanOp () : scanData_(), nextItr_(), nullT_() { nextItr_ = scanData_.end(); nullT_ = 0; } | |
inline BooleanOp (T nullT) : scanData_(), nextItr_(), nullT_(nullT) { nextItr_ = scanData_.end(); } | |
inline BooleanOp (const BooleanOp& that) : scanData_(that.scanData_), nextItr_(), | |
nullT_(that.nullT_) { nextItr_ = scanData_.begin(); } | |
inline BooleanOp& operator=(const BooleanOp& that); | |
//moves scanline forward | |
inline void advanceScan() { nextItr_ = scanData_.begin(); } | |
//proceses the given interval and T data | |
//appends output edges to cT | |
template <class cT> | |
inline void processInterval(cT& outputContainer, interval_data<Unit> ivl, T deltaCount); | |
private: | |
inline typename ScanData::iterator lookup_(Unit pos){ | |
if(nextItr_ != scanData_.end() && nextItr_->first >= pos) { | |
return nextItr_; | |
} | |
return nextItr_ = scanData_.lower_bound(pos); | |
} | |
inline typename ScanData::iterator insert_(Unit pos, T count){ | |
return nextItr_ = scanData_.insert(nextItr_, ElementType(pos, count)); | |
} | |
template <class cT> | |
inline void evaluateInterval_(cT& outputContainer, interval_data<Unit> ivl, T beforeCount, T afterCount); | |
}; | |
class BinaryAnd { | |
public: | |
inline BinaryAnd() {} | |
inline bool operator()(int a, int b) { return (a > 0) & (b > 0); } | |
}; | |
class BinaryOr { | |
public: | |
inline BinaryOr() {} | |
inline bool operator()(int a, int b) { return (a > 0) | (b > 0); } | |
}; | |
class BinaryNot { | |
public: | |
inline BinaryNot() {} | |
inline bool operator()(int a, int b) { return (a > 0) & !(b > 0); } | |
}; | |
class BinaryXor { | |
public: | |
inline BinaryXor() {} | |
inline bool operator()(int a, int b) { return (a > 0) ^ (b > 0); } | |
}; | |
//BinaryCount is an array of two deltaCounts coming from two different layers | |
//of scan event data. It is the merged count of the two suitable for consumption | |
//as the template argument of the BooleanOp algorithm because BinaryCount casts to int. | |
//T is a binary functor object that evaluates the array of counts and returns a logical | |
//result of some operation on those values. | |
//BinaryCount supports many of the operators that work with int, particularly the | |
//binary operators, but cannot support less than or increment. | |
template <class T> | |
class BinaryCount { | |
public: | |
inline BinaryCount() | |
#ifndef BOOST_POLYGON_MSVC | |
: counts_() | |
#endif | |
{ counts_[0] = counts_[1] = 0; } | |
// constructs from two integers | |
inline BinaryCount(int countL, int countR) | |
#ifndef BOOST_POLYGON_MSVC | |
: counts_() | |
#endif | |
{ counts_[0] = countL, counts_[1] = countR; } | |
inline BinaryCount& operator=(int count) { counts_[0] = count, counts_[1] = count; return *this; } | |
inline BinaryCount& operator=(const BinaryCount& that); | |
inline BinaryCount(const BinaryCount& that) | |
#ifndef BOOST_POLYGON_MSVC | |
: counts_() | |
#endif | |
{ *this = that; } | |
inline bool operator==(const BinaryCount& that) const; | |
inline bool operator!=(const BinaryCount& that) const { return !((*this) == that);} | |
inline BinaryCount& operator+=(const BinaryCount& that); | |
inline BinaryCount& operator-=(const BinaryCount& that); | |
inline BinaryCount operator+(const BinaryCount& that) const; | |
inline BinaryCount operator-(const BinaryCount& that) const; | |
inline BinaryCount operator-() const; | |
inline int& operator[](bool index) { return counts_[index]; } | |
//cast to int operator evaluates data using T binary functor | |
inline operator int() const { return T()(counts_[0], counts_[1]); } | |
private: | |
int counts_[2]; | |
}; | |
class UnaryCount { | |
public: | |
inline UnaryCount() : count_(0) {} | |
// constructs from two integers | |
inline explicit UnaryCount(int count) : count_(count) {} | |
inline UnaryCount& operator=(int count) { count_ = count; return *this; } | |
inline UnaryCount& operator=(const UnaryCount& that) { count_ = that.count_; return *this; } | |
inline UnaryCount(const UnaryCount& that) : count_(that.count_) {} | |
inline bool operator==(const UnaryCount& that) const { return count_ == that.count_; } | |
inline bool operator!=(const UnaryCount& that) const { return !((*this) == that);} | |
inline UnaryCount& operator+=(const UnaryCount& that) { count_ += that.count_; return *this; } | |
inline UnaryCount& operator-=(const UnaryCount& that) { count_ -= that.count_; return *this; } | |
inline UnaryCount operator+(const UnaryCount& that) const { UnaryCount tmp(*this); tmp += that; return tmp; } | |
inline UnaryCount operator-(const UnaryCount& that) const { UnaryCount tmp(*this); tmp -= that; return tmp; } | |
inline UnaryCount operator-() const { UnaryCount tmp; return tmp - *this; } | |
//cast to int operator evaluates data using T binary functor | |
inline operator int() const { return count_ % 2; } | |
private: | |
int count_; | |
}; | |
template <class T, typename Unit> | |
inline BooleanOp<T, Unit>& BooleanOp<T, Unit>::operator=(const BooleanOp& that) { | |
scanData_ = that.scanData_; | |
nextItr_ = scanData_.begin(); | |
nullT_ = that.nullT_; | |
return *this; | |
} | |
//appends output edges to cT | |
template <class T, typename Unit> | |
template <class cT> | |
inline void BooleanOp<T, Unit>::processInterval(cT& outputContainer, interval_data<Unit> ivl, T deltaCount) { | |
typename ScanData::iterator lowItr = lookup_(ivl.low()); | |
typename ScanData::iterator highItr = lookup_(ivl.high()); | |
//add interval to scan data if it is past the end | |
if(lowItr == scanData_.end()) { | |
lowItr = insert_(ivl.low(), deltaCount); | |
highItr = insert_(ivl.high(), nullT_); | |
evaluateInterval_(outputContainer, ivl, nullT_, deltaCount); | |
return; | |
} | |
//ensure that highItr points to the end of the ivl | |
if(highItr == scanData_.end() || (*highItr).first > ivl.high()) { | |
T value = nullT_; | |
if(highItr != scanData_.begin()) { | |
--highItr; | |
value = highItr->second; | |
} | |
nextItr_ = highItr; | |
highItr = insert_(ivl.high(), value); | |
} | |
//split the low interval if needed | |
if(lowItr->first > ivl.low()) { | |
if(lowItr != scanData_.begin()) { | |
--lowItr; | |
nextItr_ = lowItr; | |
lowItr = insert_(ivl.low(), lowItr->second); | |
} else { | |
nextItr_ = lowItr; | |
lowItr = insert_(ivl.low(), nullT_); | |
} | |
} | |
//process scan data intersecting interval | |
for(typename ScanData::iterator itr = lowItr; itr != highItr; ){ | |
T beforeCount = itr->second; | |
T afterCount = itr->second += deltaCount; | |
Unit low = itr->first; | |
++itr; | |
Unit high = itr->first; | |
evaluateInterval_(outputContainer, interval_data<Unit>(low, high), beforeCount, afterCount); | |
} | |
//merge the bottom interval with the one below if they have the same count | |
if(lowItr != scanData_.begin()){ | |
typename ScanData::iterator belowLowItr = lowItr; | |
--belowLowItr; | |
if(belowLowItr->second == lowItr->second) { | |
scanData_.erase(lowItr); | |
} | |
} | |
//merge the top interval with the one above if they have the same count | |
if(highItr != scanData_.begin()) { | |
typename ScanData::iterator beforeHighItr = highItr; | |
--beforeHighItr; | |
if(beforeHighItr->second == highItr->second) { | |
scanData_.erase(highItr); | |
highItr = beforeHighItr; | |
++highItr; | |
} | |
} | |
nextItr_ = highItr; | |
} | |
template <class T, typename Unit> | |
template <class cT> | |
inline void BooleanOp<T, Unit>::evaluateInterval_(cT& outputContainer, interval_data<Unit> ivl, | |
T beforeCount, T afterCount) { | |
bool before = (int)beforeCount > 0; | |
bool after = (int)afterCount > 0; | |
int value = (!before & after) - (before & !after); | |
if(value) { | |
outputContainer.insert(outputContainer.end(), std::pair<interval_data<Unit>, int>(ivl, value)); | |
} | |
} | |
template <class T> | |
inline BinaryCount<T>& BinaryCount<T>::operator=(const BinaryCount<T>& that) { | |
counts_[0] = that.counts_[0]; | |
counts_[1] = that.counts_[1]; | |
return *this; | |
} | |
template <class T> | |
inline bool BinaryCount<T>::operator==(const BinaryCount<T>& that) const { | |
return counts_[0] == that.counts_[0] && | |
counts_[1] == that.counts_[1]; | |
} | |
template <class T> | |
inline BinaryCount<T>& BinaryCount<T>::operator+=(const BinaryCount<T>& that) { | |
counts_[0] += that.counts_[0]; | |
counts_[1] += that.counts_[1]; | |
return *this; | |
} | |
template <class T> | |
inline BinaryCount<T>& BinaryCount<T>::operator-=(const BinaryCount<T>& that) { | |
counts_[0] += that.counts_[0]; | |
counts_[1] += that.counts_[1]; | |
return *this; | |
} | |
template <class T> | |
inline BinaryCount<T> BinaryCount<T>::operator+(const BinaryCount<T>& that) const { | |
BinaryCount retVal(*this); | |
retVal += that; | |
return retVal; | |
} | |
template <class T> | |
inline BinaryCount<T> BinaryCount<T>::operator-(const BinaryCount<T>& that) const { | |
BinaryCount retVal(*this); | |
retVal -= that; | |
return retVal; | |
} | |
template <class T> | |
inline BinaryCount<T> BinaryCount<T>::operator-() const { | |
return BinaryCount<T>() - *this; | |
} | |
template <class T, typename Unit, typename iterator_type_1, typename iterator_type_2> | |
inline void applyBooleanBinaryOp(std::vector<std::pair<Unit, std::pair<Unit, int> > >& output, | |
//const std::vector<std::pair<Unit, std::pair<Unit, int> > >& input1, | |
//const std::vector<std::pair<Unit, std::pair<Unit, int> > >& input2, | |
iterator_type_1 itr1, iterator_type_1 itr1_end, | |
iterator_type_2 itr2, iterator_type_2 itr2_end, | |
T defaultCount) { | |
BooleanOp<T, Unit> boolean(defaultCount); | |
//typename std::vector<std::pair<Unit, std::pair<Unit, int> > >::const_iterator itr1 = input1.begin(); | |
//typename std::vector<std::pair<Unit, std::pair<Unit, int> > >::const_iterator itr2 = input2.begin(); | |
std::vector<std::pair<interval_data<Unit>, int> > container; | |
//output.reserve((std::max)(input1.size(), input2.size())); | |
//consider eliminating dependecy on limits with bool flag for initial state | |
Unit UnitMax = (std::numeric_limits<Unit>::max)(); | |
Unit prevCoord = UnitMax; | |
Unit prevPosition = UnitMax; | |
T count(defaultCount); | |
//define the starting point | |
if(itr1 != itr1_end) { | |
prevCoord = (*itr1).first; | |
prevPosition = (*itr1).second.first; | |
count[0] += (*itr1).second.second; | |
} | |
if(itr2 != itr2_end) { | |
if((*itr2).first < prevCoord || | |
((*itr2).first == prevCoord && (*itr2).second.first < prevPosition)) { | |
prevCoord = (*itr2).first; | |
prevPosition = (*itr2).second.first; | |
count = defaultCount; | |
count[1] += (*itr2).second.second; | |
++itr2; | |
} else if((*itr2).first == prevCoord && (*itr2).second.first == prevPosition) { | |
count[1] += (*itr2).second.second; | |
++itr2; | |
if(itr1 != itr1_end) ++itr1; | |
} else { | |
if(itr1 != itr1_end) ++itr1; | |
} | |
} else { | |
if(itr1 != itr1_end) ++itr1; | |
} | |
while(itr1 != itr1_end || itr2 != itr2_end) { | |
Unit curCoord = UnitMax; | |
Unit curPosition = UnitMax; | |
T curCount(defaultCount); | |
if(itr1 != itr1_end) { | |
curCoord = (*itr1).first; | |
curPosition = (*itr1).second.first; | |
curCount[0] += (*itr1).second.second; | |
} | |
if(itr2 != itr2_end) { | |
if((*itr2).first < curCoord || | |
((*itr2).first == curCoord && (*itr2).second.first < curPosition)) { | |
curCoord = (*itr2).first; | |
curPosition = (*itr2).second.first; | |
curCount = defaultCount; | |
curCount[1] += (*itr2).second.second; | |
++itr2; | |
} else if((*itr2).first == curCoord && (*itr2).second.first == curPosition) { | |
curCount[1] += (*itr2).second.second; | |
++itr2; | |
if(itr1 != itr1_end) ++itr1; | |
} else { | |
if(itr1 != itr1_end) ++itr1; | |
} | |
} else { | |
++itr1; | |
} | |
if(prevCoord != curCoord) { | |
boolean.advanceScan(); | |
prevCoord = curCoord; | |
prevPosition = curPosition; | |
count = curCount; | |
continue; | |
} | |
if(curPosition != prevPosition && count != defaultCount) { | |
interval_data<Unit> ivl(prevPosition, curPosition); | |
container.clear(); | |
boolean.processInterval(container, ivl, count); | |
for(std::size_t i = 0; i < container.size(); ++i) { | |
std::pair<interval_data<Unit>, int>& element = container[i]; | |
if(!output.empty() && output.back().first == prevCoord && | |
output.back().second.first == element.first.low() && | |
output.back().second.second == element.second * -1) { | |
output.pop_back(); | |
} else { | |
output.push_back(std::pair<Unit, std::pair<Unit, int> >(prevCoord, std::pair<Unit, int>(element.first.low(), | |
element.second))); | |
} | |
output.push_back(std::pair<Unit, std::pair<Unit, int> >(prevCoord, std::pair<Unit, int>(element.first.high(), | |
element.second * -1))); | |
} | |
} | |
prevPosition = curPosition; | |
count += curCount; | |
} | |
} | |
template <class T, typename Unit> | |
inline void applyBooleanBinaryOp(std::vector<std::pair<Unit, std::pair<Unit, int> > >& inputOutput, | |
const std::vector<std::pair<Unit, std::pair<Unit, int> > >& input2, | |
T defaultCount) { | |
std::vector<std::pair<Unit, std::pair<Unit, int> > > output; | |
applyBooleanBinaryOp(output, inputOutput, input2, defaultCount); | |
if(output.size() < inputOutput.size() / 2) { | |
inputOutput = std::vector<std::pair<Unit, std::pair<Unit, int> > >(); | |
} else { | |
inputOutput.clear(); | |
} | |
inputOutput.insert(inputOutput.end(), output.begin(), output.end()); | |
} | |
template <typename Unit> | |
inline void applyUnaryXOr(std::vector<std::pair<Unit, std::pair<Unit, int> > >& input) { | |
BooleanOp<UnaryCount, Unit> booleanXOr; | |
} | |
template <typename count_type = int> | |
struct default_arg_workaround { | |
template <typename Unit> | |
static inline void applyBooleanOr(std::vector<std::pair<Unit, std::pair<Unit, int> > >& input) { | |
BooleanOp<count_type, Unit> booleanOr; | |
std::vector<std::pair<interval_data<Unit>, int> > container; | |
std::vector<std::pair<Unit, std::pair<Unit, int> > > output; | |
output.reserve(input.size()); | |
//consider eliminating dependecy on limits with bool flag for initial state | |
Unit UnitMax = (std::numeric_limits<Unit>::max)(); | |
Unit prevPos = UnitMax; | |
Unit prevY = UnitMax; | |
int count = 0; | |
for(typename std::vector<std::pair<Unit, std::pair<Unit, int> > >::iterator itr = input.begin(); | |
itr != input.end(); ++itr) { | |
Unit pos = (*itr).first; | |
Unit y = (*itr).second.first; | |
if(pos != prevPos) { | |
booleanOr.advanceScan(); | |
prevPos = pos; | |
prevY = y; | |
count = (*itr).second.second; | |
continue; | |
} | |
if(y != prevY && count != 0) { | |
interval_data<Unit> ivl(prevY, y); | |
container.clear(); | |
booleanOr.processInterval(container, ivl, count_type(count)); | |
for(std::size_t i = 0; i < container.size(); ++i) { | |
std::pair<interval_data<Unit>, int>& element = container[i]; | |
if(!output.empty() && output.back().first == prevPos && | |
output.back().second.first == element.first.low() && | |
output.back().second.second == element.second * -1) { | |
output.pop_back(); | |
} else { | |
output.push_back(std::pair<Unit, std::pair<Unit, int> >(prevPos, std::pair<Unit, int>(element.first.low(), | |
element.second))); | |
} | |
output.push_back(std::pair<Unit, std::pair<Unit, int> >(prevPos, std::pair<Unit, int>(element.first.high(), | |
element.second * -1))); | |
} | |
} | |
prevY = y; | |
count += (*itr).second.second; | |
} | |
if(output.size() < input.size() / 2) { | |
input = std::vector<std::pair<Unit, std::pair<Unit, int> > >(); | |
} else { | |
input.clear(); | |
} | |
input.insert(input.end(), output.begin(), output.end()); | |
} | |
}; | |
} | |
} | |
} | |
#endif |