/* | |
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_POLYGON_90_TOUCH_HPP | |
#define BOOST_POLYGON_POLYGON_90_TOUCH_HPP | |
namespace boost { namespace polygon{ | |
template <typename Unit> | |
struct touch_90_operation { | |
typedef interval_data<Unit> Interval; | |
class TouchScanEvent { | |
private: | |
typedef std::map<Unit, std::set<int> > EventData; | |
EventData eventData_; | |
public: | |
// The TouchScanEvent::iterator is a lazy algorithm that accumulates | |
// polygon ids in a set as it is incremented through the | |
// scan event data structure. | |
// The iterator provides a forward iterator semantic only. | |
class iterator { | |
private: | |
typename EventData::const_iterator itr_; | |
std::pair<Interval, std::set<int> > ivlIds_; | |
bool incremented_; | |
public: | |
inline iterator() : itr_(), ivlIds_(), incremented_(false) {} | |
inline iterator(typename EventData::const_iterator itr, | |
Unit prevPos, Unit curPos, const std::set<int>& ivlIds) : itr_(itr), ivlIds_(), incremented_(false) { | |
ivlIds_.second = ivlIds; | |
ivlIds_.first = Interval(prevPos, curPos); | |
} | |
inline iterator(const iterator& that) : itr_(), ivlIds_(), incremented_(false) { (*this) = that; } | |
inline iterator& operator=(const iterator& that) { | |
itr_ = that.itr_; | |
ivlIds_.first = that.ivlIds_.first; | |
ivlIds_.second = that.ivlIds_.second; | |
incremented_ = that.incremented_; | |
return *this; | |
} | |
inline bool operator==(const iterator& that) { return itr_ == that.itr_; } | |
inline bool operator!=(const iterator& that) { return itr_ != that.itr_; } | |
inline iterator& operator++() { | |
//std::cout << "increment\n"; | |
//std::cout << "state\n"; | |
//for(std::set<int>::iterator itr = ivlIds_.second.begin(); itr != ivlIds_.second.end(); ++itr) { | |
// std::cout << (*itr) << " "; | |
//} std::cout << std::endl; | |
//std::cout << "update\n"; | |
for(std::set<int>::const_iterator itr = (*itr_).second.begin(); | |
itr != (*itr_).second.end(); ++itr) { | |
//std::cout << (*itr) << " "; | |
std::set<int>::iterator lb = ivlIds_.second.find(*itr); | |
if(lb != ivlIds_.second.end()) { | |
ivlIds_.second.erase(lb); | |
} else { | |
ivlIds_.second.insert(*itr); | |
} | |
} | |
//std::cout << std::endl; | |
//std::cout << "new state\n"; | |
//for(std::set<int>::iterator itr = ivlIds_.second.begin(); itr != ivlIds_.second.end(); ++itr) { | |
// std::cout << (*itr) << " "; | |
//} std::cout << std::endl; | |
++itr_; | |
//ivlIds_.first = Interval(ivlIds_.first.get(HIGH), itr_->first); | |
incremented_ = true; | |
return *this; | |
} | |
inline const iterator operator++(int){ | |
iterator tmpItr(*this); | |
++(*this); | |
return tmpItr; | |
} | |
inline std::pair<Interval, std::set<int> >& operator*() { | |
if(incremented_) ivlIds_.first = Interval(ivlIds_.first.get(HIGH), itr_->first); | |
incremented_ = false; | |
if(ivlIds_.second.empty())(++(*this)); | |
if(incremented_) ivlIds_.first = Interval(ivlIds_.first.get(HIGH), itr_->first); | |
incremented_ = false; | |
return ivlIds_; } | |
}; | |
inline TouchScanEvent() : eventData_() {} | |
template<class iT> | |
inline TouchScanEvent(iT begin, iT end) : eventData_() { | |
for( ; begin != end; ++begin){ | |
insert(*begin); | |
} | |
} | |
inline TouchScanEvent(const TouchScanEvent& that) : eventData_(that.eventData_) {} | |
inline TouchScanEvent& operator=(const TouchScanEvent& that){ | |
eventData_ = that.eventData_; | |
return *this; | |
} | |
//Insert an interval polygon id into the EventData | |
inline void insert(const std::pair<Interval, int>& intervalId){ | |
insert(intervalId.first.low(), intervalId.second); | |
insert(intervalId.first.high(), intervalId.second); | |
} | |
//Insert an position and polygon id into EventData | |
inline void insert(Unit pos, int id) { | |
typename EventData::iterator lb = eventData_.lower_bound(pos); | |
if(lb != eventData_.end() && lb->first == pos) { | |
std::set<int>& mr (lb->second); | |
std::set<int>::iterator mri = mr.find(id); | |
if(mri == mr.end()) { | |
mr.insert(id); | |
} else { | |
mr.erase(id); | |
} | |
} else { | |
lb = eventData_.insert(lb, std::pair<Unit, std::set<int> >(pos, std::set<int>())); | |
(*lb).second.insert(id); | |
} | |
} | |
//merge this scan event with that by inserting its data | |
inline void insert(const TouchScanEvent& that){ | |
typename EventData::const_iterator itr; | |
for(itr = that.eventData_.begin(); itr != that.eventData_.end(); ++itr) { | |
eventData_[(*itr).first].insert(itr->second.begin(), itr->second.end()); | |
} | |
} | |
//Get the begin iterator over event data | |
inline iterator begin() const { | |
//std::cout << "begin\n"; | |
if(eventData_.empty()) return end(); | |
typename EventData::const_iterator itr = eventData_.begin(); | |
Unit pos = itr->first; | |
const std::set<int>& idr = itr->second; | |
++itr; | |
return iterator(itr, pos, itr->first, idr); | |
} | |
//Get the end iterator over event data | |
inline iterator end() const { return iterator(eventData_.end(), 0, 0, std::set<int>()); } | |
inline void clear() { eventData_.clear(); } | |
inline Interval extents() const { | |
if(eventData_.empty()) return Interval(); | |
return Interval((*(eventData_.begin())).first, (*(eventData_.rbegin())).first); | |
} | |
}; | |
//declaration of a map of scan events by coordinate value used to store all the | |
//polygon data for a single layer input into the scanline algorithm | |
typedef std::pair<std::map<Unit, TouchScanEvent>, std::map<Unit, TouchScanEvent> > TouchSetData; | |
class TouchOp { | |
public: | |
typedef std::map<Unit, std::set<int> > ScanData; | |
typedef std::pair<Unit, std::set<int> > ElementType; | |
protected: | |
ScanData scanData_; | |
typename ScanData::iterator nextItr_; | |
public: | |
inline TouchOp () : scanData_(), nextItr_() { nextItr_ = scanData_.end(); } | |
inline TouchOp (const TouchOp& that) : scanData_(that.scanData_), nextItr_() { nextItr_ = scanData_.begin(); } | |
inline TouchOp& operator=(const TouchOp& that); | |
//moves scanline forward | |
inline void advanceScan() { nextItr_ = scanData_.begin(); } | |
//proceses the given interval and std::set<int> data | |
//the output data structre is a graph, the indicies in the vector correspond to graph nodes, | |
//the integers in the set are vector indicies and are the nodes with which that node shares an edge | |
template <typename graphT> | |
inline void processInterval(graphT& outputContainer, Interval ivl, const std::set<int>& ids, bool leadingEdge) { | |
//print(); | |
typename ScanData::iterator lowItr = lookup_(ivl.low()); | |
typename ScanData::iterator highItr = lookup_(ivl.high()); | |
//std::cout << "Interval: " << ivl << std::endl; | |
//for(std::set<int>::const_iterator itr = ids.begin(); itr != ids.end(); ++itr) | |
// std::cout << (*itr) << " "; | |
//std::cout << std::endl; | |
//add interval to scan data if it is past the end | |
if(lowItr == scanData_.end()) { | |
//std::cout << "case0" << std::endl; | |
lowItr = insert_(ivl.low(), ids); | |
evaluateBorder_(outputContainer, ids, ids); | |
highItr = insert_(ivl.high(), std::set<int>()); | |
return; | |
} | |
//ensure that highItr points to the end of the ivl | |
if(highItr == scanData_.end() || (*highItr).first > ivl.high()) { | |
//std::cout << "case1" << std::endl; | |
//std::cout << highItr->first << std::endl; | |
std::set<int> value = std::set<int>(); | |
if(highItr != scanData_.begin()) { | |
--highItr; | |
//std::cout << highItr->first << std::endl; | |
//std::cout << "high set size " << highItr->second.size() << std::endl; | |
value = highItr->second; | |
} | |
nextItr_ = highItr; | |
highItr = insert_(ivl.high(), value); | |
} else { | |
//evaluate border with next higher interval | |
//std::cout << "case1a" << std::endl; | |
if(leadingEdge)evaluateBorder_(outputContainer, highItr->second, ids); | |
} | |
//split the low interval if needed | |
if(lowItr->first > ivl.low()) { | |
//std::cout << "case2" << std::endl; | |
if(lowItr != scanData_.begin()) { | |
//std::cout << "case3" << std::endl; | |
--lowItr; | |
nextItr_ = lowItr; | |
//std::cout << lowItr->first << " " << lowItr->second.size() << std::endl; | |
lowItr = insert_(ivl.low(), lowItr->second); | |
} else { | |
//std::cout << "case4" << std::endl; | |
nextItr_ = lowItr; | |
lowItr = insert_(ivl.low(), std::set<int>()); | |
} | |
} else { | |
//evaluate border with next higher interval | |
//std::cout << "case2a" << std::endl; | |
typename ScanData::iterator nextLowerItr = lowItr; | |
if(leadingEdge && nextLowerItr != scanData_.begin()){ | |
--nextLowerItr; | |
evaluateBorder_(outputContainer, nextLowerItr->second, ids); | |
} | |
} | |
//std::cout << "low: " << lowItr->first << " high: " << highItr->first << std::endl; | |
//print(); | |
//process scan data intersecting interval | |
for(typename ScanData::iterator itr = lowItr; itr != highItr; ){ | |
//std::cout << "case5" << std::endl; | |
//std::cout << itr->first << std::endl; | |
std::set<int>& beforeIds = itr->second; | |
++itr; | |
evaluateInterval_(outputContainer, beforeIds, ids, leadingEdge); | |
} | |
//print(); | |
//merge the bottom interval with the one below if they have the same count | |
if(lowItr != scanData_.begin()){ | |
//std::cout << "case6" << std::endl; | |
typename ScanData::iterator belowLowItr = lowItr; | |
--belowLowItr; | |
if(belowLowItr->second == lowItr->second) { | |
//std::cout << "case7" << std::endl; | |
scanData_.erase(lowItr); | |
} | |
} | |
//merge the top interval with the one above if they have the same count | |
if(highItr != scanData_.begin()) { | |
//std::cout << "case8" << std::endl; | |
typename ScanData::iterator beforeHighItr = highItr; | |
--beforeHighItr; | |
if(beforeHighItr->second == highItr->second) { | |
//std::cout << "case9" << std::endl; | |
scanData_.erase(highItr); | |
highItr = beforeHighItr; | |
++highItr; | |
} | |
} | |
//print(); | |
nextItr_ = highItr; | |
} | |
// inline void print() const { | |
// for(typename ScanData::const_iterator itr = scanData_.begin(); itr != scanData_.end(); ++itr) { | |
// std::cout << itr->first << ": "; | |
// for(std::set<int>::const_iterator sitr = itr->second.begin(); | |
// sitr != itr->second.end(); ++sitr){ | |
// std::cout << *sitr << " "; | |
// } | |
// std::cout << std::endl; | |
// } | |
// } | |
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, const std::set<int>& ids){ | |
//std::cout << "inserting " << ids.size() << " ids at: " << pos << std::endl; | |
return nextItr_ = scanData_.insert(nextItr_, std::pair<Unit, std::set<int> >(pos, ids)); | |
} | |
template <typename graphT> | |
inline void evaluateInterval_(graphT& outputContainer, std::set<int>& ids, | |
const std::set<int>& changingIds, bool leadingEdge) { | |
for(std::set<int>::const_iterator ciditr = changingIds.begin(); ciditr != changingIds.end(); ++ciditr){ | |
//std::cout << "evaluateInterval " << (*ciditr) << std::endl; | |
evaluateId_(outputContainer, ids, *ciditr, leadingEdge); | |
} | |
} | |
template <typename graphT> | |
inline void evaluateBorder_(graphT& outputContainer, const std::set<int>& ids, const std::set<int>& changingIds) { | |
for(std::set<int>::const_iterator ciditr = changingIds.begin(); ciditr != changingIds.end(); ++ciditr){ | |
//std::cout << "evaluateBorder " << (*ciditr) << std::endl; | |
evaluateBorderId_(outputContainer, ids, *ciditr); | |
} | |
} | |
template <typename graphT> | |
inline void evaluateBorderId_(graphT& outputContainer, const std::set<int>& ids, int changingId) { | |
for(std::set<int>::const_iterator scanItr = ids.begin(); scanItr != ids.end(); ++scanItr) { | |
//std::cout << "create edge: " << changingId << " " << *scanItr << std::endl; | |
if(changingId != *scanItr){ | |
outputContainer[changingId].insert(*scanItr); | |
outputContainer[*scanItr].insert(changingId); | |
} | |
} | |
} | |
template <typename graphT> | |
inline void evaluateId_(graphT& outputContainer, std::set<int>& ids, int changingId, bool leadingEdge) { | |
//std::cout << "changingId: " << changingId << std::endl; | |
//for( std::set<int>::iterator itr = ids.begin(); itr != ids.end(); ++itr){ | |
// std::cout << *itr << " "; | |
//}std::cout << std::endl; | |
std::set<int>::iterator lb = ids.lower_bound(changingId); | |
if(lb == ids.end() || (*lb) != changingId) { | |
if(leadingEdge) { | |
//std::cout << "insert\n"; | |
//insert and add to output | |
for(std::set<int>::iterator scanItr = ids.begin(); scanItr != ids.end(); ++scanItr) { | |
//std::cout << "create edge: " << changingId << " " << *scanItr << std::endl; | |
if(changingId != *scanItr){ | |
outputContainer[changingId].insert(*scanItr); | |
outputContainer[*scanItr].insert(changingId); | |
} | |
} | |
ids.insert(changingId); | |
} | |
} else { | |
if(!leadingEdge){ | |
//std::cout << "erase\n"; | |
ids.erase(lb); | |
} | |
} | |
} | |
}; | |
template <typename graphT> | |
static inline void processEvent(graphT& outputContainer, TouchOp& op, const TouchScanEvent& data, bool leadingEdge) { | |
for(typename TouchScanEvent::iterator itr = data.begin(); itr != data.end(); ++itr) { | |
//std::cout << "processInterval" << std::endl; | |
op.processInterval(outputContainer, (*itr).first, (*itr).second, leadingEdge); | |
} | |
} | |
template <typename graphT> | |
static inline void performTouch(graphT& outputContainer, const TouchSetData& data) { | |
typename std::map<Unit, TouchScanEvent>::const_iterator leftItr = data.first.begin(); | |
typename std::map<Unit, TouchScanEvent>::const_iterator rightItr = data.second.begin(); | |
typename std::map<Unit, TouchScanEvent>::const_iterator leftEnd = data.first.end(); | |
typename std::map<Unit, TouchScanEvent>::const_iterator rightEnd = data.second.end(); | |
TouchOp op; | |
while(leftItr != leftEnd || rightItr != rightEnd) { | |
//std::cout << "loop" << std::endl; | |
op.advanceScan(); | |
//rightItr cannont be at end if leftItr is not at end | |
if(leftItr != leftEnd && rightItr != rightEnd && | |
leftItr->first <= rightItr->first) { | |
//std::cout << "case1" << std::endl; | |
//std::cout << leftItr ->first << std::endl; | |
processEvent(outputContainer, op, leftItr->second, true); | |
++leftItr; | |
} else { | |
//std::cout << "case2" << std::endl; | |
//std::cout << rightItr ->first << std::endl; | |
processEvent(outputContainer, op, rightItr->second, false); | |
++rightItr; | |
} | |
} | |
} | |
template <class iT> | |
static inline void populateTouchSetData(TouchSetData& data, iT beginData, iT endData, int id) { | |
Unit prevPos = ((std::numeric_limits<Unit>::max)()); | |
Unit prevY = prevPos; | |
int count = 0; | |
for(iT itr = beginData; itr != endData; ++itr) { | |
Unit pos = (*itr).first; | |
if(pos != prevPos) { | |
prevPos = pos; | |
prevY = (*itr).second.first; | |
count = (*itr).second.second; | |
continue; | |
} | |
Unit y = (*itr).second.first; | |
if(count != 0 && y != prevY) { | |
std::pair<Interval, int> element(Interval(prevY, y), id); | |
if(count > 0) { | |
data.first[pos].insert(element); | |
} else { | |
data.second[pos].insert(element); | |
} | |
} | |
prevY = y; | |
count += (*itr).second.second; | |
} | |
} | |
static inline void populateTouchSetData(TouchSetData& data, const std::vector<std::pair<Unit, std::pair<Unit, int> > >& inputData, int id) { | |
populateTouchSetData(data, inputData.begin(), inputData.end(), id); | |
} | |
}; | |
} | |
} | |
#endif |