/* 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_PROPERTY_MERGE_HPP #define BOOST_POLYGON_PROPERTY_MERGE_HPP namespace boost { namespace polygon{ template class property_merge_point { private: coordinate_type x_, y_; public: inline property_merge_point() : x_(), y_() {} inline property_merge_point(coordinate_type x, coordinate_type y) : x_(x), y_(y) {} //use builtin assign and copy inline bool operator==(const property_merge_point& that) const { return x_ == that.x_ && y_ == that.y_; } inline bool operator!=(const property_merge_point& that) const { return !((*this) == that); } inline bool operator<(const property_merge_point& that) const { if(x_ < that.x_) return true; if(x_ > that.x_) return false; return y_ < that.y_; } inline coordinate_type x() const { return x_; } inline coordinate_type y() const { return y_; } inline void x(coordinate_type value) { x_ = value; } inline void y(coordinate_type value) { y_ = value; } }; template class property_merge_interval { private: coordinate_type low_, high_; public: inline property_merge_interval() : low_(), high_() {} inline property_merge_interval(coordinate_type low, coordinate_type high) : low_(low), high_(high) {} //use builtin assign and copy inline bool operator==(const property_merge_interval& that) const { return low_ == that.low_ && high_ == that.high_; } inline bool operator!=(const property_merge_interval& that) const { return !((*this) == that); } inline bool operator<(const property_merge_interval& that) const { if(low_ < that.low_) return true; if(low_ > that.low_) return false; return high_ < that.high_; } inline coordinate_type low() const { return low_; } inline coordinate_type high() const { return high_; } inline void low(coordinate_type value) { low_ = value; } inline void high(coordinate_type value) { high_ = value; } }; template > class merge_scanline { public: //definitions typedef keytype property_set; typedef std::vector > property_map; typedef std::pair, std::pair > vertex_property; typedef std::pair, property_map> vertex_data; typedef std::vector property_merge_data; //typedef std::map Result; typedef std::map scanline_type; typedef typename scanline_type::iterator scanline_iterator; typedef std::pair, std::pair > edge_property; typedef std::vector edge_property_vector; //static public member functions template static inline void populate_property_merge_data(property_merge_data& pmd, iT input_begin, iT input_end, const property_type& property, orientation_2d_type orient) { for( ; input_begin != input_end; ++input_begin) { std::pair, std::pair > element; if(orient == HORIZONTAL) element.first = property_merge_point((*input_begin).second.first, (*input_begin).first); else element.first = property_merge_point((*input_begin).first, (*input_begin).second.first); element.second.first = property; element.second.second = (*input_begin).second.second; pmd.push_back(element); } } //public member functions merge_scanline() : output(), scanline(), currentVertex(), tmpVector(), previousY(), countFromBelow(), scanlinePosition() {} merge_scanline(const merge_scanline& that) : output(that.output), scanline(that.scanline), currentVertex(that.currentVertex), tmpVector(that.tmpVector), previousY(that.previousY), countFromBelow(that.countFromBelow), scanlinePosition(that.scanlinePosition) {} merge_scanline& operator=(const merge_scanline& that) { output = that.output; scanline = that.scanline; currentVertex = that.currentVertex; tmpVector = that.tmpVector; previousY = that.previousY; countFromBelow = that.countFromBelow; scanlinePosition = that.scanlinePosition; return *this; } template inline void perform_merge(result_type& result, property_merge_data& data) { if(data.empty()) return; //sort polygon_sort(data.begin(), data.end(), less_vertex_data()); //scanline bool firstIteration = true; scanlinePosition = scanline.end(); for(std::size_t i = 0; i < data.size(); ++i) { if(firstIteration) { mergeProperty(currentVertex.second, data[i].second); currentVertex.first = data[i].first; firstIteration = false; } else { if(data[i].first != currentVertex.first) { if(data[i].first.x() != currentVertex.first.x()) { processVertex(output); //std::cout << scanline.size() << " "; countFromBelow.clear(); //should already be clear writeOutput(currentVertex.first.x(), result, output); currentVertex.second.clear(); mergeProperty(currentVertex.second, data[i].second); currentVertex.first = data[i].first; //std::cout << assertRedundant(scanline) << "/" << scanline.size() << " "; } else { processVertex(output); currentVertex.second.clear(); mergeProperty(currentVertex.second, data[i].second); currentVertex.first = data[i].first; } } else { mergeProperty(currentVertex.second, data[i].second); } } } processVertex(output); writeOutput(currentVertex.first.x(), result, output); //std::cout << assertRedundant(scanline) << "/" << scanline.size() << "\n"; //std::cout << scanline.size() << "\n"; } private: //private supporting types template class less_vertex_data { public: less_vertex_data() {} bool operator()(const T& lvalue, const T& rvalue) const { if(lvalue.first.x() < rvalue.first.x()) return true; if(lvalue.first.x() > rvalue.first.x()) return false; if(lvalue.first.y() < rvalue.first.y()) return true; return false; } }; template struct lessPropertyCount { lessPropertyCount() {} bool operator()(const T& a, const T& b) { return a.first < b.first; } }; //private static member functions static inline void mergeProperty(property_map& lvalue, std::pair& rvalue) { typename property_map::iterator itr = std::lower_bound(lvalue.begin(), lvalue.end(), rvalue, lessPropertyCount >()); if(itr == lvalue.end() || (*itr).first != rvalue.first) { lvalue.insert(itr, rvalue); } else { (*itr).second += rvalue.second; if((*itr).second == 0) lvalue.erase(itr); } // if(assertSorted(lvalue)) { // std::cout << "in mergeProperty\n"; // exit(0); // } } // static inline bool assertSorted(property_map& pset) { // bool result = false; // for(std::size_t i = 1; i < pset.size(); ++i) { // if(pset[i] < pset[i-1]) { // std::cout << "Out of Order Error "; // result = true; // } // if(pset[i].first == pset[i-1].first) { // std::cout << "Duplicate Property Error "; // result = true; // } // if(pset[0].second == 0 || pset[1].second == 0) { // std::cout << "Empty Property Error "; // result = true; // } // } // return result; // } static inline void setProperty(property_set& pset, property_map& pmap) { for(typename property_map::iterator itr = pmap.begin(); itr != pmap.end(); ++itr) { if((*itr).second > 0) { pset.insert(pset.end(), (*itr).first); } } } //private data members edge_property_vector output; scanline_type scanline; vertex_data currentVertex; property_map tmpVector; coordinate_type previousY; property_map countFromBelow; scanline_iterator scanlinePosition; //private member functions inline void mergeCount(property_map& lvalue, property_map& rvalue) { typename property_map::iterator litr = lvalue.begin(); typename property_map::iterator ritr = rvalue.begin(); tmpVector.clear(); while(litr != lvalue.end() && ritr != rvalue.end()) { if((*litr).first <= (*ritr).first) { if(!tmpVector.empty() && (*litr).first == tmpVector.back().first) { tmpVector.back().second += (*litr).second; } else { tmpVector.push_back(*litr); } ++litr; } else if((*ritr).first <= (*litr).first) { if(!tmpVector.empty() && (*ritr).first == tmpVector.back().first) { tmpVector.back().second += (*ritr).second; } else { tmpVector.push_back(*ritr); } ++ritr; } } while(litr != lvalue.end()) { if(!tmpVector.empty() && (*litr).first == tmpVector.back().first) { tmpVector.back().second += (*litr).second; } else { tmpVector.push_back(*litr); } ++litr; } while(ritr != rvalue.end()) { if(!tmpVector.empty() && (*ritr).first == tmpVector.back().first) { tmpVector.back().second += (*ritr).second; } else { tmpVector.push_back(*ritr); } ++ritr; } lvalue.clear(); for(std::size_t i = 0; i < tmpVector.size(); ++i) { if(tmpVector[i].second != 0) { lvalue.push_back(tmpVector[i]); } } // if(assertSorted(lvalue)) { // std::cout << "in mergeCount\n"; // exit(0); // } } inline void processVertex(edge_property_vector& output) { if(!countFromBelow.empty()) { //we are processing an interval of change in scanline state between //previous vertex position and current vertex position where //count from below represents the change on the interval //foreach scanline element from previous to current we //write the interval on the scanline that is changing //the old value and the new value to output property_merge_interval currentInterval(previousY, currentVertex.first.y()); coordinate_type currentY = currentInterval.low(); if(scanlinePosition == scanline.end() || (*scanlinePosition).first != previousY) { scanlinePosition = scanline.lower_bound(previousY); } scanline_iterator previousScanlinePosition = scanlinePosition; ++scanlinePosition; while(scanlinePosition != scanline.end()) { coordinate_type elementY = (*scanlinePosition).first; if(elementY <= currentInterval.high()) { property_map& countOnLeft = (*previousScanlinePosition).second; edge_property element; output.push_back(element); output.back().first = property_merge_interval((*previousScanlinePosition).first, elementY); setProperty(output.back().second.first, countOnLeft); mergeCount(countOnLeft, countFromBelow); setProperty(output.back().second.second, countOnLeft); if(output.back().second.first == output.back().second.second) { output.pop_back(); //it was an internal vertical edge, not to be output } else if(output.size() > 1) { edge_property& secondToLast = output[output.size()-2]; if(secondToLast.first.high() == output.back().first.low() && secondToLast.second.first == output.back().second.first && secondToLast.second.second == output.back().second.second) { //merge output onto previous output because properties are //identical on both sides implying an internal horizontal edge secondToLast.first.high(output.back().first.high()); output.pop_back(); } } if(previousScanlinePosition == scanline.begin()) { if(countOnLeft.empty()) { scanline.erase(previousScanlinePosition); } } else { scanline_iterator tmpitr = previousScanlinePosition; --tmpitr; if((*tmpitr).second == (*previousScanlinePosition).second) scanline.erase(previousScanlinePosition); } } else if(currentY < currentInterval.high()){ //elementY > currentInterval.high() //split the interval between previous and current scanline elements std::pair elementScan; elementScan.first = currentInterval.high(); elementScan.second = (*previousScanlinePosition).second; scanlinePosition = scanline.insert(scanlinePosition, elementScan); continue; } else { break; } previousScanlinePosition = scanlinePosition; currentY = previousY = elementY; ++scanlinePosition; if(scanlinePosition == scanline.end() && currentY < currentInterval.high()) { //insert a new element for top of range std::pair elementScan; elementScan.first = currentInterval.high(); scanlinePosition = scanline.insert(scanline.end(), elementScan); } } if(scanlinePosition == scanline.end() && currentY < currentInterval.high()) { //handle case where we iterated to end of the scanline //we need to insert an element into the scanline at currentY //with property value coming from below //and another one at currentInterval.high() with empty property value mergeCount(scanline[currentY], countFromBelow); std::pair elementScan; elementScan.first = currentInterval.high(); scanline.insert(scanline.end(), elementScan); edge_property element; output.push_back(element); output.back().first = property_merge_interval(currentY, currentInterval.high()); setProperty(output.back().second.second, countFromBelow); mergeCount(countFromBelow, currentVertex.second); } else { mergeCount(countFromBelow, currentVertex.second); if(countFromBelow.empty()) { if(previousScanlinePosition == scanline.begin()) { if((*previousScanlinePosition).second.empty()) { scanline.erase(previousScanlinePosition); //previousScanlinePosition = scanline.end(); //std::cout << "ERASE_A "; } } else { scanline_iterator tmpitr = previousScanlinePosition; --tmpitr; if((*tmpitr).second == (*previousScanlinePosition).second) { scanline.erase(previousScanlinePosition); //previousScanlinePosition = scanline.end(); //std::cout << "ERASE_B "; } } } } } else { //count from below is empty, we are starting a new interval of change countFromBelow = currentVertex.second; scanlinePosition = scanline.lower_bound(currentVertex.first.y()); if(scanlinePosition != scanline.end()) { if((*scanlinePosition).first != currentVertex.first.y()) { if(scanlinePosition != scanline.begin()) { //decrement to get the lower position of the first interval this vertex intersects --scanlinePosition; //insert a new element into the scanline for the incoming vertex property_map& countOnLeft = (*scanlinePosition).second; std::pair element(currentVertex.first.y(), countOnLeft); scanlinePosition = scanline.insert(scanlinePosition, element); } else { property_map countOnLeft; std::pair element(currentVertex.first.y(), countOnLeft); scanlinePosition = scanline.insert(scanlinePosition, element); } } } else { property_map countOnLeft; std::pair element(currentVertex.first.y(), countOnLeft); scanlinePosition = scanline.insert(scanlinePosition, element); } } previousY = currentVertex.first.y(); } template inline int assertRedundant(T& t) { if(t.empty()) return 0; int count = 0; typename T::iterator itr = t.begin(); if((*itr).second.empty()) ++count; typename T::iterator itr2 = itr; ++itr2; while(itr2 != t.end()) { if((*itr).second == (*itr2).second) ++count; itr = itr2; ++itr2; } return count; } template inline void performExtract(T& result, property_merge_data& data) { if(data.empty()) return; //sort polygon_sort(data.begin(), data.end(), less_vertex_data()); //scanline bool firstIteration = true; scanlinePosition = scanline.end(); for(std::size_t i = 0; i < data.size(); ++i) { if(firstIteration) { mergeProperty(currentVertex.second, data[i].second); currentVertex.first = data[i].first; firstIteration = false; } else { if(data[i].first != currentVertex.first) { if(data[i].first.x() != currentVertex.first.x()) { processVertex(output); //std::cout << scanline.size() << " "; countFromBelow.clear(); //should already be clear writeGraph(result, output, scanline); currentVertex.second.clear(); mergeProperty(currentVertex.second, data[i].second); currentVertex.first = data[i].first; } else { processVertex(output); currentVertex.second.clear(); mergeProperty(currentVertex.second, data[i].second); currentVertex.first = data[i].first; } } else { mergeProperty(currentVertex.second, data[i].second); } } } processVertex(output); writeGraph(result, output, scanline); //std::cout << scanline.size() << "\n"; } template inline void insertEdges(T& graph, property_set& p1, property_set& p2) { for(typename property_set::iterator itr = p1.begin(); itr != p1.end(); ++itr) { for(typename property_set::iterator itr2 = p2.begin(); itr2 != p2.end(); ++itr2) { if(*itr != *itr2) { graph[*itr].insert(*itr2); graph[*itr2].insert(*itr); } } } } template inline void propertySetAbove(coordinate_type y, property_set& ps, T& scanline) { ps.clear(); typename T::iterator itr = scanline.find(y); if(itr != scanline.end()) setProperty(ps, (*itr).second); } template inline void propertySetBelow(coordinate_type y, property_set& ps, T& scanline) { ps.clear(); typename T::iterator itr = scanline.find(y); if(itr != scanline.begin()) { --itr; setProperty(ps, (*itr).second); } } template inline void writeGraph(T& graph, edge_property_vector& output, T2& scanline) { if(output.empty()) return; edge_property* previousEdgeP = &(output[0]); bool firstIteration = true; property_set ps; for(std::size_t i = 0; i < output.size(); ++i) { edge_property& previousEdge = *previousEdgeP; edge_property& edge = output[i]; if(previousEdge.first.high() == edge.first.low()) { //horizontal edge insertEdges(graph, edge.second.first, previousEdge.second.first); //corner 1 insertEdges(graph, edge.second.first, previousEdge.second.second); //other horizontal edge insertEdges(graph, edge.second.second, previousEdge.second.second); //corner 2 insertEdges(graph, edge.second.second, previousEdge.second.first); } else { if(!firstIteration){ //look up regions above previous edge propertySetAbove(previousEdge.first.high(), ps, scanline); insertEdges(graph, ps, previousEdge.second.first); insertEdges(graph, ps, previousEdge.second.second); } //look up regions below current edge in the scanline propertySetBelow(edge.first.high(), ps, scanline); insertEdges(graph, ps, edge.second.first); insertEdges(graph, ps, edge.second.second); } firstIteration = false; //vertical edge insertEdges(graph, edge.second.second, edge.second.first); //shared region to left insertEdges(graph, edge.second.second, edge.second.second); //shared region to right insertEdges(graph, edge.second.first, edge.second.first); previousEdgeP = &(output[i]); } edge_property& previousEdge = *previousEdgeP; propertySetAbove(previousEdge.first.high(), ps, scanline); insertEdges(graph, ps, previousEdge.second.first); insertEdges(graph, ps, previousEdge.second.second); output.clear(); } template inline void writeOutput(coordinate_type x, Result& result, edge_property_vector& output) { for(std::size_t i = 0; i < output.size(); ++i) { edge_property& edge = output[i]; //edge.second.first is the property set on the left of the edge if(!edge.second.first.empty()) { typename Result::iterator itr = result.find(edge.second.first); if(itr == result.end()) { std::pair element(edge.second.first, polygon_set_type(VERTICAL)); itr = result.insert(result.end(), element); } std::pair, int> element2(interval_data(edge.first.low(), edge.first.high()), -1); //right edge of figure (*itr).second.insert(x, element2); } if(!edge.second.second.empty()) { //edge.second.second is the property set on the right of the edge typename Result::iterator itr = result.find(edge.second.second); if(itr == result.end()) { std::pair element(edge.second.second, polygon_set_type(VERTICAL)); itr = result.insert(result.end(), element); } std::pair, int> element3(interval_data(edge.first.low(), edge.first.high()), 1); //left edge of figure (*itr).second.insert(x, element3); } } output.clear(); } }; } } #endif