/* ---- This file is part of SECONDO. Copyright (C) 2017, University in Hagen, Faculty of Mathematics and Computer Science, Database Systems for New Applications. SECONDO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. SECONDO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- //[_] [\_] */ #ifndef AVLSEGMENTIMPL_H #define AVLSEGMENTIMPL_H #include "RobustSetOps.h" #include "AvlTree.h" #include "Tools/Flob/DbArray.h" /* 1 Implementation of class AVLSegment. */ class XRemover : public unary_functor{ public: XRemover(): value(0.0) {} XRemover(const XRemover& src): value(src.value) {} XRemover& operator=(const XRemover& src){ value = src.value; return *this; } void setX(double x){ value = x; } bool operator()(const avlseg::AVLSegment& s) const{ double x2 = s.getX2(); return !AlmostEqual(value,x2) && x2 < value; } private: double value; }; void insertEvents(const avlseg::AVLSegment& seg, const bool createLeft, const bool createRight, std::priority_queue, std::greater >& q1, std::priority_queue, std::greater >& q2); bool splitByNeighbour(avltree::AVLTree& sss, avlseg::AVLSegment& current, avlseg::AVLSegment *& neighbour, std::priority_queue, std::greater >& q1, std::priority_queue, std::greater >& q2, const bool forceThrow ); void splitNeighbours(avltree::AVLTree& sss, avlseg::AVLSegment *& leftN, avlseg::AVLSegment *& rightN, std::priority_queue, std::greater >& q1, std::priority_queue, std::greater >& q2, const bool forceThrow); /* checks whether the dominating points within the halfsegments are sorted correctly. */ template bool checkOrder(const Array& segs, bool exact){ if(segs.Size() < 2){ return true; } Point p1(false); Point p2(false); HalfSegment hs1; HalfSegment hs2; segs.Get(0,hs1); p1 = hs1.GetDomPoint(); for(int i=1;i avlseg::ownertype selectNext(const T1& v1, int& pos1, const T2& v2, int& pos2, std::priority_queue, std::greater >& q1, std::priority_queue, std::greater >& q2, avlseg::ExtendedHalfSegment& result, int src = 0 ){ const avlseg::ExtendedHalfSegment* values[4]; HalfSegment hs0hs, hs2hs; avlseg::ExtendedHalfSegment hs0, hs1, hs2, hs3; int number = 0; // number of available values // read the available elements if(pos1isInitialized()){ std::cerr << "selectNext returns an undefined extened HalfSegment" << std::endl; std::cerr << "the index is " << i << std::endl; values[i]->Print(std::cerr) << std::endl; assert(values[i]->isInitialized()); } if(values[i]){ if(index<0 || (*values[index] > *values[i])){ index = i; } } } result = *values[index]; src = index + 1; switch(index){ case 0: pos1++; return avlseg::first; case 1: q1.pop(); return avlseg::first; case 2: pos2++; return avlseg::second; case 3: q2.pop(); return avlseg::second; default: assert(false); } return avlseg::none; } template class Array1, template class Array2> avlseg::ownertype selectNext(const RegionT& reg1, int& pos1, const RegionT& reg2, int& pos2, std::priority_queue, std::greater >& q1, std::priority_queue, std::greater >& q2, avlseg::ExtendedHalfSegment& result, int& src // for debugging only ){ return selectNext,RegionT >(reg1,pos1,reg2,pos2,q1, q2,result,src); } /* 7.8 ~line~ [x] ~line~ Instantiation of the ~selectNext~ function. */ template class Array1, template class Array2> avlseg::ownertype selectNext(const LineT& line1, int& pos1, const LineT& line2, int& pos2, std::priority_queue, std::greater >& q1, std::priority_queue, std::greater >& q2, avlseg::ExtendedHalfSegment& result, int& src ){ return selectNext,LineT >(line1,pos1,line2, pos2,q1,q2,result, src); } /* 7.9 ~line~ [x] ~region~ ~selectNext~ Instantiation of the ~selectNext~ function for ~line~ [x] ~region~. */ template class Array1, template class Array2> avlseg::ownertype selectNext(const LineT& line, int& pos1, const RegionT& region, int& pos2, std::priority_queue, std::greater >& q1, std::priority_queue, std::greater >& q2, avlseg::ExtendedHalfSegment& result, int& src ){ return selectNext,RegionT >(line,pos1,region,pos2, q1,q2,result,src); } /* ~SelectNext~ line [x] point This function looks which event from the line or from the point is smaller. The line is divided into the original line part at position ~posLine~ and possible splitted segments stored in ~q~. The return value of the function will be ~first~ if the next event comes from the line value and ~second~ if the next event comes from the point value. Depending of the return value, one of the arguments ~resHs~ or ~resPoint~ is set the the value of this event. The positions are increased automatically by this function. */ template class Array> avlseg::ownertype selectNext( const LineT& line, std::priority_queue, std::greater >& q, int& posLine, const Point& point, int& posPoint, // >0: point already used avlseg::ExtendedHalfSegment& resHs, Point& resPoint){ int size = line.Size(); avlseg::ExtendedHalfSegment hsl; bool hs1exists = false; avlseg::ExtendedHalfSegment hsq; bool hsqexists = false; avlseg::ExtendedHalfSegment hsmin; bool hsminexists = false; avlseg::ExtendedHalfSegment hstmp; int src = 0; if(posLine < size){ HalfSegment hsl_transfer; line.Get(posLine,hsl_transfer); hsl = hsl_transfer; hs1exists = true; } if(!q.empty()){ hstmp = q.top(); hsq = hstmp; hsqexists = true; } if(hs1exists){ src = 1; hsmin = hsl; hsminexists = true; } if(hsqexists){ if(!hs1exists || (hsq < hsl)){ src = 2; hsmin = hsq; hsminexists = true; } } if(posPoint==0){ // point not already used if(!hsminexists){ src = 3; } else { Point p = hsmin.GetDomPoint(); if(point < p){ src = 3; } } } switch(src){ case 0: return avlseg::none; case 1: posLine++; resHs = hsmin; return avlseg::first; case 2: q.pop(); resHs = hsmin; return avlseg::first; case 3: resPoint = point; posPoint++; return avlseg::second; default: assert(false); return avlseg::none; } } /* ~selectNext~ line [x] points This function works like the function above but instead for a point, a points value is used. */ template class Array> avlseg::ownertype selectNext(const LineT& line, std::priority_queue, std::greater >& q, int& posLine, const PointsT& point, int& posPoint, avlseg::ExtendedHalfSegment& resHs, Point& resPoint){ int sizeP = point.Size(); int sizeL = line.Size(); avlseg::ExtendedHalfSegment hsl; bool hslexists = false; avlseg::ExtendedHalfSegment hsq; bool hsqexists = false; avlseg::ExtendedHalfSegment hsmin; bool hsminexists = false; avlseg::ExtendedHalfSegment hstmp; int src = 0; if(posLine < sizeL){ HalfSegment hsl_transfer; line.Get(posLine,hsl_transfer); hsl = hsl_transfer; hslexists = true; } if(!q.empty()){ hstmp = q.top(); hsq = hstmp; hsqexists = true; } if(hslexists){ src = 1; hsmin = hsl; hsminexists = true; } if(hsqexists){ if(!hslexists || (hsq < hsl)){ src = 2; hsmin = hsq; hsminexists = true; } } Point cp; if(posPoint class Array> avlseg::ownertype selectNext(const LineT& src, int& pos, std::priority_queue, std::greater >& q, avlseg::ExtendedHalfSegment& result){ int size = src.Size(); if(size<=pos){ if(q.empty()){ return avlseg::none; } else { result = q.top(); q.pop(); return avlseg::first; } } else { HalfSegment hs1; src.Get(pos,hs1); avlseg::ExtendedHalfSegment hs(hs1); if(q.empty()){ result = hs; pos++; return avlseg::first; } else{ avlseg::ExtendedHalfSegment hsq = q.top(); if(hsq class Array1, template class Array2> void Realminize2(const LineT& src, LineT& result, const bool forceThrow=false){ result.Clear(); if(!src.IsDefined()){ result.SetDefined(false); return; } result.SetDefined(true); if(src.Size()==0){ // empty line, nothing to realminize return; } std::priority_queue, std::greater > q; avltree::AVLTree sss; int pos = 0; avlseg::ExtendedHalfSegment nextHS; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1, right1,left2,right2; result.StartBulkLoad(); int edgeno = 0; avlseg::AVLSegment tmpL,tmpR; while(selectNext(src,pos,q,nextHS)!=avlseg::none) { avlseg::AVLSegment current(nextHS,avlseg::first); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHS.IsLeftDomPoint()){ if(member){ // overlapping segment found in sss double xm = member->getX2(); double xc = current.getX2(); if(!AlmostEqual(xm,xc) && (xmgetY2(),left1,right1); insertEvents(right1,true,true,q,q); } } else { // no overlapping segment found splitByNeighbour(sss,current,leftN,q,q, forceThrow); splitByNeighbour(sss,current,rightN,q,q, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q,q); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the halfsegments avlseg::ExtendedHalfSegment hs1 = current.convertToExtendedHs(true); avlseg::ExtendedHalfSegment hs2 = current.convertToExtendedHs(false); hs1.attr.edgeno = edgeno; hs2.attr.edgeno = edgeno; result += hs1; result += hs2; splitNeighbours(sss,leftN,rightN,q,q, forceThrow); edgeno++; sss.remove(*member); } } } result.EndBulkLoad(); } // Realminize2 template class Array> avlseg::ownertype selectNext(const Array& src, int& pos, std::priority_queue, std::greater >& q, avlseg::ExtendedHalfSegment& result){ int size = src.Size(); if(size<=pos){ if(q.empty()){ return avlseg::none; } else { result = q.top(); q.pop(); return avlseg::first; } } else { HalfSegment hs1; src.Get(pos,hs1); avlseg::ExtendedHalfSegment hs(hs1); if(q.empty()){ result = hs; pos++; return avlseg::first; } else{ avlseg::ExtendedHalfSegment hsq = q.top(); if(hsq class Array> Array* Realminize(const Array& segments, const bool forceThrow = false, const bool robust = false){ Array* res = new Array(segments.Size()); if(robust){ res->clean(); robust::realminize(segments,*res); } else { try { if(segments.Size()==0){ // no halfsegments, nothing to realminize res->TrimToSize(); return res; } std::priority_queue, std::greater > q1; // dummy queue std::priority_queue, std::greater > q2; avltree::AVLTree sss; int pos = 0; avlseg::ExtendedHalfSegment nextHS; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1, right1,left2,right2; int edgeno = 0; avlseg::AVLSegment tmpL,tmpR,tmpM; XRemover xRemover; while(selectNext(segments,pos,q1,nextHS)!=avlseg::none) { avlseg::AVLSegment current(nextHS,avlseg::first); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(member){ tmpM = *member; member = &tmpM; } if(nextHS.IsLeftDomPoint()){ if(member && member->overlaps(current)){ // overlapping segment found in sss double xm = member->getX2(); double xc = current.getX2(); if(!AlmostEqual(xm,xc) && (xmAppend(hs1); res->Append(hs2); splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow); edgeno++; sss.remove(*member); insertEvents(current,true,true,q1,q2); } else { current.splitAtRight(xm,member->getY2(),right1); insertEvents(right1,true,true,q1,q2); } } else if(AlmostEqual(xm,xc) && current.isVertical()){ double ym = member->getY2(); double yc = current.getY2(); if(!AlmostEqual(ym,yc) && (ym < yc)){ current.splitAtRight(xc,yc,right1); insertEvents(right1,true,true,q1,q2); } } } else { // no overlapping segment found if(splitByNeighbour(sss,current,leftN,q1,q2, forceThrow)){ insertEvents(current,true,true,q1,q2); } else if(splitByNeighbour(sss,current,rightN,q1,q2, forceThrow)) { insertEvents(current,true,true,q1,q2); } else { sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the halfsegments HalfSegment hs1 = current.convertToExtendedHs(true); HalfSegment hs2 = current.convertToExtendedHs(false); hs1.attr.edgeno = edgeno; hs2.attr.edgeno = edgeno; res->Append(hs1); res->Append(hs2); splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow); edgeno++; sss.remove(*member); } } if(avlseg::AVLSegment::isError()){ std::cerr << "error during comparision detected" << std::endl; avltree::AVLTree::iterator it = sss.begin(); double val = avlseg::AVLSegment::getErrorValue(); std::vector evilsegments; cout << "start iterating" << std::endl; unsigned int size = sss.Size(); cout << "The tree has " << size << " entries" << std::endl; unsigned int b=0; while(!it.onEnd()){ b++; avlseg::AVLSegment* seg = it.Get(); double x2 = seg->getX2(); if(!AlmostEqual(x2,val) && x2 < val){ evilsegments.push_back(seg); } it++; assert(b<=size); } if(evilsegments.size() > 0){ cout << "recognized " << evilsegments.size() << " evil segments" << std::endl; cout << "error_value = " << val << std::endl; unsigned int count = 0; for(unsigned int i=0; i< evilsegments.size();i++){ avlseg::AVLSegment* seg = evilsegments[i]; HalfSegment hs1 = seg->convertToExtendedHs(true); HalfSegment hs2 = seg->convertToExtendedHs(false); hs1.attr.edgeno = edgeno; hs2.attr.edgeno = edgeno; res->Append(hs1); res->Append(hs2); bool ok = sss.remove(*seg); edgeno++; evilsegments[i] = 0; if(!ok){ count++; } } if(count != 0){ cout << "some (" << count << " of the evil segments was not found, scan the tree" << " to remove them" << std::endl; xRemover.setX(val); cout << "Start removeAll" << std::endl; unsigned int count2 = sss.removeAll(xRemover); if(count != count2){ cout << count << " elements should be remove, but only " << count2 << " was found " << std::endl; } } cout << "segments removed" << std::endl; } else { cout << "evil segment already processed" << std::endl; } avlseg::AVLSegment::clearError(); } } if(sss.Size()!=0){ cout << " After planesweep, the status structure is not empty ! " << std::endl; } } catch (...){ std::cerr << "Realminize via plane sweep failed, switch to " "slower (robust) implementation" << std::endl; res->clean(); robust::realminize(segments,*res); } } res->Sort(HalfSegmentCompare); res->TrimToSize(); return res; } template class Array> bool hasOverlaps(const Array& segments, const bool ignoreEqual, const bool forceThrow = false){ if(segments.Size()<2){ // no overlaps possible return false; } std::priority_queue, std::greater > q; avltree::AVLTree sss; int pos = 0; avlseg::ExtendedHalfSegment nextHS; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1, right1,left2,right2; avlseg::AVLSegment tmpL,tmpR; while(selectNext(segments,pos,q,nextHS)!=avlseg::none) { avlseg::AVLSegment current(nextHS,avlseg::first); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHS.IsLeftDomPoint()){ if(member){ // overlapping segment found in sss if(!ignoreEqual || !member->exactEqualsTo(current)){ return true; } double xm = member->getX2(); double xc = current.getX2(); if(!AlmostEqual(xm,xc) && (xmgetY2(),right1); insertEvents(right1,true,true,q,q); } } else { // no overlapping segment found splitByNeighbour(sss,current,leftN,q,q, forceThrow); splitByNeighbour(sss,current,rightN,q,q, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q,q); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the halfsegments splitNeighbours(sss,leftN,rightN,q,q, forceThrow); sss.remove(*member); } } } return false; } /* ~Split~ This function works similar to the realminize function. The difference is, that overlapping parts of segments are kept, instead of beeig removed. But at all crossing points and so on, the segments will be split. */ template class Array> Array* Split(const Array& segments, const bool forceThrow =false){ Array* res = new Array(0); if(segments.Size()<2){ // no intersecting halfsegments res->TrimToSize(); return res; } std::priority_queue, std::greater > q; avltree::AVLTree sss; int pos = 0; avlseg::ExtendedHalfSegment nextHS; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1, right1,left2,right2; int edgeno = 0; avlseg::AVLSegment tmpL,tmpR; while(selectNext(segments,pos,q,nextHS)!=avlseg::none) { avlseg::AVLSegment current(nextHS,avlseg::first); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHS.IsLeftDomPoint()){ if(member){ // overlapping segment found in sss // insert the common part into res avlseg::AVLSegment tmp_left, tmp_common, tmp_right; uint32_t sr = member->split(current,tmp_left, tmp_common,tmp_right,false); tmp_left.setOwner(avlseg::first); tmp_common.setOwner(avlseg::first); tmp_right.setOwner(avlseg::first); Point pl(true,tmp_common.getX1(),tmp_common.getY1()); Point pr(true,tmp_common.getX2(),tmp_common.getY2()); HalfSegment hs1 = tmp_common.convertToExtendedHs(true); HalfSegment hs2 = tmp_common.convertToExtendedHs(false); hs1.attr.edgeno = edgeno; hs2.attr.edgeno = edgeno; res->Append(hs1); res->Append(hs2); edgeno++; sss.remove(*member); if(sr & avlseg::LEFT){ if(!tmp_left.isPoint()){ sss.insert(tmp_left); insertEvents(tmp_left,false,true,q,q); } } insertEvents(tmp_common,true,true,q,q); if(sr & avlseg::RIGHT){ insertEvents(tmp_right,true,true,q,q); } } else { // no overlapping segment found splitByNeighbour(sss,current,leftN,q,q, forceThrow); splitByNeighbour(sss,current,rightN,q,q, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q,q); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the halfsegments HalfSegment hs1 = current.convertToExtendedHs(true); HalfSegment hs2 = current.convertToExtendedHs(false); hs1.attr.edgeno = edgeno; hs2.attr.edgeno = edgeno; res->Append(hs1); res->Append(hs2); splitNeighbours(sss,leftN,rightN,q,q, forceThrow); edgeno++; sss.remove(*member); } } } res->Sort(HalfSegmentCompare); res->TrimToSize(); // work around because problems with overlapping segments if(hasOverlaps(*res,true)){ Array* tmp = Split(*res); delete res; res = tmp; } return res; } /* 9.2 ~line~ [x] ~line~ [->] ~line~ This combination can be used for all possible set operations. */ template class Array1, template class Array2, template class Array3> void SetOp(const LineT& line1, const LineT& line2, LineT& result, avlseg::SetOperation op, const Geoid* geoid=0, const bool forceThrow =false){ result.Clear(); if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical geometry case } if(!line1.IsDefined() || !line2.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); if(line1.Size()==0){ switch(op){ case avlseg::union_op : result = line2; return; case avlseg::intersection_op : return; // empty line case avlseg::difference_op : return; // empty line default : assert(false); } } if(line2.Size()==0){ switch(op){ case avlseg::union_op: result = line1; return; case avlseg::intersection_op: return; case avlseg::difference_op: result = line1; return; default : assert(false); } } std::priority_queue, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1, left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; result.StartBulkLoad(); while( (owner=selectNext(line1,pos1, line2,pos2, q1,q2,nextHs,src))!=avlseg::none){ avlseg::AVLSegment current(nextHs,owner); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // found an overlapping segment if(member->getOwner()==current.getOwner() || member->getOwner()==avlseg::both){ // same source double xm = member->getX2(); double xc = current.getX2(); if(!AlmostEqual(xm,xc) && (xmgetY2(),right1); insertEvents(right1,true,true,q1,q2); } } else { // member and current come from different sources uint32_t parts = member->split(current,left1,common1,right1); sss.remove(*member); member = &common1; if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); if(!common1.isPoint()){ sss.insert(common1); insertEvents(common1,false,true,q1,q2); } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } } else { // no overlapping segment found splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the segments into the result switch(op){ case avlseg::union_op : { HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; break; } case avlseg::intersection_op : { if(member->getOwner()==avlseg::both){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } case avlseg::difference_op :{ if(member->getOwner()==avlseg::first){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } default : { assert(false); } } sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow); } } } result.EndBulkLoad(true,false); } // setop line x line -> line template class Array1, template class Array2> LineT* SetOp(const LineT& line1, const LineT& line2, avlseg::SetOperation op, const Geoid* geoid=0){ LineT* result = new LineT(1); SetOp(line1,line2,*result,op,geoid); return result; } /* 9.3 ~region~ [x] ~region~ [->] ~region~ */ template class Array1, template class Array2, template class Array3> void SetOp(const RegionT& reg1, const RegionT& reg2, RegionT& result, avlseg::SetOperation op, const Geoid* geoid=0, const bool forceThrow=false){ if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical case } result.Clear(); if(!reg1.IsDefined() || !reg2.IsDefined() || (geoid && !geoid->IsDefined())){ result.SetDefined(false); return; } result.SetDefined(true); if(reg1.Size()==0){ switch(op){ case avlseg::union_op : result = reg2; return; case avlseg::intersection_op : return; // empty region case avlseg::difference_op : return; // empty region default : assert(false); } } if(reg2.Size()==0){ switch(op){ case avlseg::union_op: result = reg1; return; case avlseg::intersection_op: return; case avlseg::difference_op: result = reg1; return; default : assert(false); } } if(!reg1.BoundingBox().Intersects(reg2.BoundingBox(),geoid)){ switch(op){ case avlseg::union_op: { result.StartBulkLoad(); int edgeno=0; int s = reg1.Size(); HalfSegment hs; for(int i=0;i, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member = 0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1, left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; result.StartBulkLoad(); while( (owner=selectNext(reg1,pos1, reg2,pos2, q1,q2,nextHs,src))!=avlseg::none){ avlseg::AVLSegment current(nextHs,owner); avlseg::AVLSegment currentCopy(current); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // overlapping segment found if((member->getOwner()==avlseg::both) || (member->getOwner()==owner)){ std::cerr << "overlapping segments detected within a " << "single region" << std::endl; std::cerr << "the argument is " << (owner==avlseg::first?"first":"second") << std::endl; std::cerr.precision(16); std::cerr << "stored is " << *member << std::endl; std::cerr << "current = " << current << std::endl; avlseg::AVLSegment tmp_left, tmp_common, tmp_right; member->split(current,tmp_left, tmp_common, tmp_right, false); std::cerr << "The common part is " << tmp_common << std::endl; std::cerr << "The lenth = " << tmp_common.length() << std::endl; assert(false); } uint32_t parts = member->split(current,left1,common1,right1,true); sss.remove(*member); if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); // update coverage numbers if(current.getInsideAbove()){ common1.con_above++; } else { common1.con_above--; } if(!common1.isPoint()){ sss.insert(common1); if((parts & avlseg::LEFT) || (parts & avlseg::RIGHT)){ insertEvents(common1,false,true,q1,q2); } // otherwise the original is not changed } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } else { // there is no overlapping segment // try to split segments if required splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); // update coverage numbers bool iac = current.getOwner()==avlseg::first ?current.getInsideAbove_first() :current.getInsideAbove_second(); iac = current.getOwner()==avlseg::first ?current.getInsideAbove_first() :current.getInsideAbove_second(); if(leftN && current.extends(*leftN)){ current.con_below = leftN->con_below; current.con_above = leftN->con_above; }else{ if(leftN && leftN->isVertical()){ current.con_below = leftN->con_below; } else if(leftN){ current.con_below = leftN->con_above; } else { current.con_below = 0; } if(iac){ current.con_above = current.con_below+1; } else { current.con_above = current.con_below-1; } } // insert element if it was changed if(!current.isPoint()){ sss.insert(current); if(!current.exactEqualsTo(currentCopy)){ insertEvents(current,false,true,q1,q2); } } } } else { // nextHs.IsRightDomPoint if(member && member->exactEqualsTo(current)){ switch(op){ case avlseg::union_op :{ if( (member->con_above==0) || (member->con_below==0)) { HalfSegment hs1 = member->getOwner()==avlseg::both ?member->convertToExtendedHs(true,avlseg::first) :member->convertToExtendedHs(true); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } case avlseg::intersection_op: { if(member->con_above==2 || member->con_below==2){ HalfSegment hs1 = member->getOwner()==avlseg::both ?member->convertToExtendedHs(true,avlseg::first) :member->convertToExtendedHs(true); hs1.attr.edgeno = edgeno; hs1.attr.insideAbove = (member->con_above==2); result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } case avlseg::difference_op : { switch(member->getOwner()){ case avlseg::first:{ if(member->con_above + member->con_below == 1){ HalfSegment hs1 = member->getOwner()==avlseg::both ?member->convertToExtendedHs(true,avlseg::first) :member->convertToExtendedHs(true); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } case avlseg::second:{ if(member->con_above + member->con_below == 3){ HalfSegment hs1 = member->getOwner()==avlseg::both ?member->convertToExtendedHs(true,avlseg::second) :member->convertToExtendedHs(true); hs1.attr.insideAbove = ! hs1.attr.insideAbove; hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } case avlseg::both: { if((member->con_above==1) && (member->con_below== 1)){ HalfSegment hs1 = member->getOwner()==avlseg::both ?member->convertToExtendedHs(true,avlseg::first) :member->convertToExtendedHs(true); hs1.attr.insideAbove = member->getInsideAbove_first(); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } default : assert(false); } // switch member->getOwner break; } // case difference default : assert(false); } // end of switch sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow); } // current found in sss } // right endpoint } result.EndBulkLoad(); } // setOP region x region -> region template class Array> RegionT* SetOp(const RegionT& reg1, const RegionT& reg2, avlseg::SetOperation op, const Geoid* geoid=0, const bool forceThrow=false){ RegionT* result = new RegionT(1); SetOp(reg1,reg2,*result,op,geoid); return result; } /* 9 ~CommonBorder~ Signature: ~region~ [x] ~region~ [->] ~line~ */ template class Array> void CommonBorder( const RegionT& reg1, const RegionT& reg2, LineT& result, const Geoid* geoid=0, const bool forceThrow=false){ result.Clear(); if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical geometry case } if(!reg1.IsDefined() || !reg2.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); if(reg1.Size()==0 || reg2.Size()==0){ // a region is empty -> the common border is also empty return; } if(!reg1.BoundingBox().Intersects(reg2.BoundingBox())){ // no common border possible return; } std::priority_queue, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1, left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; result.StartBulkLoad(); bool done = false; int size1 = reg1.Size(); int size2 = reg2.Size(); while( ((owner=selectNext(reg1,pos1, reg2,pos2, q1,q2,nextHs,src))!=avlseg::none) && !done ){ avlseg::AVLSegment current(nextHs,owner); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // overlapping segment found assert(member->getOwner()!=avlseg::both); assert(member->getOwner()!=owner); uint32_t parts = member->split(current,left1,common1,right1); sss.remove(*member); if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); if(!common1.isPoint()){ sss.insert(common1); insertEvents(common1,false,true,q1,q2); } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } else { // there is no overlapping segment // try to split segments if required splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHs.IsRightDomPoint if(member && member->exactEqualsTo(current)){ if(member->getOwner()==avlseg::both){ HalfSegment hs = member->convertToExtendedHs(true,avlseg::first); hs.attr.edgeno = edgeno; result += hs; hs.SetLeftDomPoint(false); result += hs; edgeno++; } sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2,forceThrow); } // current found in sss if(((pos1 >= size1) && q1.empty()) || ((pos2 >= size2) && q2.empty())){ done = true; } } // right endpoint } result.EndBulkLoad(); } // commonborder /* 9.4 ~region~ [x] ~line~ [->] ~region~ This combination can only be used for the operations ~union~ and ~difference~. In both cases, the result will be the original region value. */ template class Array1, template class Array2, template class Array3> void SetOp(const RegionT& region, const LineT& line, RegionT& result, avlseg::SetOperation op, const Geoid* geoid=0){ assert(op == avlseg::union_op || op == avlseg::difference_op); result.Clear(); if(!line.IsDefined() || !region.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); result.CopyFrom(®ion); } /* 9.5 ~line~ [x] ~region~ [->] ~line~ Here, only the ~difference~ and ~intersection~ operation are applicable. */ template class Array1, template class Array2, template class Array3> void SetOp(const LineT& line, const RegionT& region, LineT& result, avlseg::SetOperation op, const Geoid* geoid=0, const bool forceThrow =false){ assert(op==avlseg::intersection_op || op == avlseg::difference_op); if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical geometry case } result.Clear(); if(!line.IsDefined() || !region.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); if(line.Size()==0){ // empty line -> empty result switch(op){ case avlseg::intersection_op : return; // empty region case avlseg::difference_op : return; // empty region default : assert(false); } } if(region.Size()==0){ switch(op){ case avlseg::intersection_op: return; case avlseg::difference_op: result = line; return; default : assert(false); } } // set result to a highly estimated value to avoid // frequent enlargements of the underlying DbArray result.Resize(line.Size()); std::priority_queue, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; int size1= line.Size(); avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1, left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; bool done = false; result.StartBulkLoad(); // perform a planesweeo while( ((owner=selectNext(line,pos1, region,pos2, q1,q2,nextHs,src))!=avlseg::none) && ! done){ avlseg::AVLSegment current(nextHs,owner); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // there is an overlapping segment in sss if(member->getOwner()==owner || member->getOwner()==avlseg::both ){ if(current.ininterior(member->getX2(),member->getY2())){ current.splitAtRight(member->getX2(),member->getY2(),right1); insertEvents(right1,true,true,q1,q2); } } else { // member and source come from difference sources uint32_t parts = member->split(current,left1,common1,right1); sss.remove(*member); member = &common1; if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); if(owner==avlseg::second) { // the region if(current.getInsideAbove()){ common1.con_above++; } else { common1.con_above--; } } // for a line is nothing to do if(!common1.isPoint()){ sss.insert(common1); insertEvents(common1,false,true,q1,q2); } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } } else { // no overlapping segment in sss found splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); // update coverage numbers if(owner==avlseg::second){ // the region bool iac = current.getInsideAbove(); if(leftN && current.extends(*leftN)){ current.con_below = leftN->con_below; current.con_above = leftN->con_above; }else{ if(leftN && leftN->isVertical()){ current.con_below = leftN->con_below; } else if(leftN){ current.con_below = leftN->con_above; } else { current.con_below = 0; } if(iac){ current.con_above = current.con_below+1; } else { current.con_above = current.con_below-1; } } } else { // the line if(leftN){ if(leftN->isVertical()){ current.con_below = leftN->con_below; } else { current.con_below = leftN->con_above; } } current.con_above = current.con_below; } // insert element if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHs.IsRightDomPoint() if(member && member->exactEqualsTo(current)){ switch(op){ case avlseg::intersection_op: { if( (member->getOwner()==avlseg::both) || (member->getOwner()==avlseg::first && member->con_above>0)){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } case avlseg::difference_op: { if( (member->getOwner()==avlseg::first) && (member->con_above==0)){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } default : assert(false); } sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow); } if(pos1>=size1 && q1.empty()){ // line is processed done = true; } } } result.EndBulkLoad(); } // setOP(line x region -> line) /* line x sline -> sline Here is only ~intersection~ applicable. */ template class Array1, template class Array2, template class Array3> void SetOp(const LineT& line1, const SimpleLineT& line2, SimpleLineT& result, avlseg::SetOperation op, const Geoid* geoid =0, const bool forceThrow=false){ result.Clear(); if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical geometry case } if(!line1.IsDefined() || !line2.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); if(line1.Size() == 0 || line2.Size() == 0) return; // empty line std::priority_queue, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1, left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; result.StartBulkLoad(); while((owner=selectNext(line1,pos1,line2,pos2, q1,q2,nextHs,src)) != avlseg::none){ avlseg::AVLSegment current(nextHs,owner); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // found an overlapping segment if(member->getOwner()==current.getOwner() || member->getOwner()==avlseg::both){ // same source double xm = member->getX2(); double xc = current.getX2(); if(!AlmostEqual(xm,xc) && (xmgetY2(),right1); insertEvents(right1,true,true,q1,q2); } } else { // member and current come from different sources uint32_t parts = member->split(current,left1,common1,right1); sss.remove(*member); member = &common1; if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); if(!common1.isPoint()){ sss.insert(common1); insertEvents(common1,false,true,q1,q2); } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } } else { // no overlapping segment found splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the segments into the result if(member->getOwner()==avlseg::both){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow); } } } result.EndBulkLoad(); } /* line x sline -> line applicable for difference and union */ template class Array1, template class Array2, template class Array3> void SetOp(const LineT& line1, const SimpleLineT& line2, LineT& result, avlseg::SetOperation op, const Geoid* geoid =0, const bool forceThrow=false){ result.Clear(); if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical geometry case } if(!line1.IsDefined() || !line2.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); if(line1.Size()==0){ switch(op){ case avlseg::union_op : line2.toLine(result);; return; case avlseg::difference_op : return; // empty line default : assert(false); } } if(line2.Size()==0){ switch(op){ case avlseg::union_op: result = line1; return; case avlseg::difference_op: result = line1; return; default : assert(false); } } std::priority_queue, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1,left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; result.StartBulkLoad(); while( (owner=selectNext(line1,pos1,line2,pos2, q1,q2,nextHs,src)) != avlseg::none){ avlseg::AVLSegment current(nextHs,owner); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // found an overlapping segment if(member->getOwner()==current.getOwner() || member->getOwner()==avlseg::both){ // same source double xm = member->getX2(); double xc = current.getX2(); if(!AlmostEqual(xm,xc) && (xmgetY2(),right1); insertEvents(right1,true,true,q1,q2); } } else { // member and current come from different sources uint32_t parts = member->split(current,left1,common1,right1); sss.remove(*member); member = &common1; if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); if(!common1.isPoint()){ sss.insert(common1); insertEvents(common1,false,true,q1,q2); } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } } else { // no overlapping segment found splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the segments into the result switch(op){ case avlseg::union_op : { HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; break; } case avlseg::difference_op :{ if(member->getOwner()==avlseg::first){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } default : { assert(false); } } sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow); } } } result.EndBulkLoad(true,false); } /* sline x line -> sline applicable for intersection and difference */ template class Array1, template class Array2, template class Array3> void SetOp(const SimpleLineT& line1, const LineT& line2, SimpleLineT& result, avlseg::SetOperation op, const Geoid* geoid =0, const bool forceThrow=false){ result.Clear(); if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical geometry case } if(!line1.IsDefined() || !line2.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); if(line1.Size()==0){ switch(op){ case avlseg::intersection_op : return; // empty line case avlseg::difference_op : return; // empty line default : assert(false); } } if(line2.Size()==0){ switch(op){ case avlseg::intersection_op: return; case avlseg::difference_op: result = line1; return; default : assert(false); } } std::priority_queue, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1, left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; result.StartBulkLoad(); while( (owner=selectNext(line1,pos1,line2,pos2,q1,q2,nextHs,src)) != avlseg::none){ avlseg::AVLSegment current(nextHs,owner); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // found an overlapping segment if(member->getOwner()==current.getOwner() || member->getOwner()==avlseg::both){ // same source double xm = member->getX2(); double xc = current.getX2(); if(!AlmostEqual(xm,xc) && (xmgetY2(),right1); insertEvents(right1,true,true,q1,q2); } } else { // member and current come from different sources uint32_t parts = member->split(current,left1,common1,right1); sss.remove(*member); member = &common1; if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); if(!common1.isPoint()){ sss.insert(common1); insertEvents(common1,false,true,q1,q2); } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } } else { // no overlapping segment found splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the segments into the result switch(op){ case avlseg::intersection_op : { if(member->getOwner()==avlseg::both){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } case avlseg::difference_op :{ if(member->getOwner()==avlseg::first){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } default : { assert(false); } } sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow); } } } result.EndBulkLoad(); } /* sline x line -> line for union only */ template class Array1, template class Array2, template class Array3> void SetOp(const SimpleLineT& line1, const LineT& line2, LineT& result, avlseg::SetOperation op, const Geoid* geoid=0, const bool forceThrow=false) { result.Clear(); if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical geometry case } if(!line1.IsDefined() || !line2.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); if(line1.Size()==0){ result = line2; return; } if(line2.Size()==0){ line1.toLine(result); return; } std::priority_queue, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1,left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; result.StartBulkLoad(); while( (owner=selectNext(line1,pos1,line2,pos2,q1,q2,nextHs,src)) != avlseg::none){ avlseg::AVLSegment current(nextHs,owner); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // found an overlapping segment if(member->getOwner()==current.getOwner() || member->getOwner()==avlseg::both){ // same source double xm = member->getX2(); double xc = current.getX2(); if(!AlmostEqual(xm,xc) && (xmgetY2(),right1); insertEvents(right1,true,true,q1,q2); } } else { // member and current come from different sources uint32_t parts = member->split(current,left1,common1,right1); sss.remove(*member); member = &common1; if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); if(!common1.isPoint()){ sss.insert(common1); insertEvents(common1,false,true,q1,q2); } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } } else { // no overlapping segment found splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the segments into the result HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2, forceThrow); } } result.EndBulkLoad(true,false); } /* sline x sline -> sline for ~intersection~ and ~minus~ */ template class Array1, template class Array2, template class Array3> void SetOp(const SimpleLineT& line1, const SimpleLineT& line2, SimpleLineT& result, avlseg::SetOperation op, const Geoid* geoid =0, const bool forceThrow=false) { result.Clear(); if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical geometry case } if(!line1.IsDefined() || !line2.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); if(line1.Size()==0){ switch(op){ case avlseg::intersection_op : return; // empty line case avlseg::difference_op : return; // empty line default : assert(false); } } if(line2.Size()==0){ switch(op){ case avlseg::intersection_op: return; case avlseg::difference_op: result = line1; return; default : assert(false); } } std::priority_queue, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1, left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; result.StartBulkLoad(); while( (owner=selectNext(line1,pos1,line2,pos2,q1,q2,nextHs,src)) != avlseg::none){ avlseg::AVLSegment current(nextHs,owner); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // found an overlapping segment if(member->getOwner()==current.getOwner() || member->getOwner()==avlseg::both){ // same source double xm = member->getX2(); double xc = current.getX2(); if(!AlmostEqual(xm,xc) && (xmgetY2(),right1); insertEvents(right1,true,true,q1,q2); } } else { // member and current come from different sources uint32_t parts = member->split(current,left1,common1,right1); sss.remove(*member); member = &common1; if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); if(!common1.isPoint()){ sss.insert(common1); insertEvents(common1,false,true,q1,q2); } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } } else { // no overlapping segment found splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the segments into the result switch(op){ case avlseg::intersection_op : { if(member->getOwner()==avlseg::both){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } case avlseg::difference_op :{ if(member->getOwner()==avlseg::first){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } default : { assert(false); } } sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2,forceThrow); } } } result.EndBulkLoad(); } /* sline x sline -> line for union only */ template class Array1, template class Array2, template class Array3> void SetOp(const SimpleLineT& line1, const SimpleLineT& line2, LineT& result, avlseg::SetOperation op, const Geoid* geoid =0, const bool forceThrow=false){ result.Clear(); if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical geometry case } if(!line1.IsDefined() || !line2.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); if(line1.Size()==0){ line2.toLine(result); return; } if(line2.Size()==0){ line1.toLine(result); return; } std::priority_queue, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1, left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; result.StartBulkLoad(); while( (owner=selectNext(line1,pos1, line2,pos2,q1,q2,nextHs,src)) != avlseg::none){ avlseg::AVLSegment current(nextHs,owner); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // found an overlapping segment if(member->getOwner()==current.getOwner() || member->getOwner()==avlseg::both){ // same source double xm = member->getX2(); double xc = current.getX2(); if(!AlmostEqual(xm,xc) && (xmgetY2(),right1); insertEvents(right1,true,true,q1,q2); } } else { // member and current come from different sources uint32_t parts = member->split(current,left1,common1,right1); sss.remove(*member); member = &common1; if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); if(!common1.isPoint()){ sss.insert(common1); insertEvents(common1,false,true,q1,q2); } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } } else { // no overlapping segment found splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHS rightDomPoint if(member && member->exactEqualsTo(current)){ // insert the segments into the result HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2,forceThrow); } } result.EndBulkLoad(true,false); } /* region x sline -> region for union and difference the result is always the given region. */ template class Array1, template class Array2, template class Array3> void SetOp(const RegionT& region, const SimpleLineT& line, RegionT& result, avlseg::SetOperation op, const Geoid* geoid =0){ assert(op == avlseg::union_op || op == avlseg::difference_op); result.Clear(); if(!line.IsDefined() || !region.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); result.CopyFrom(®ion); } /* sline x region -> sline for intersection and difference only. */ template class Array1, template class Array2, template class Array3> void SetOp(const SimpleLineT& line, const RegionT& region, SimpleLineT& result, avlseg::SetOperation op, const Geoid* geoid =0, const bool forceThrow = false){ assert(op==avlseg::intersection_op || op == avlseg::difference_op); if(geoid){ std::cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." << std::endl; assert( false ); // TODO: implement spherical geometry case } result.Clear(); if(!line.IsDefined() || !region.IsDefined() || (geoid && !geoid->IsDefined()) ){ result.SetDefined(false); return; } result.SetDefined(true); if(line.Size()==0){ // empty line -> empty result switch(op){ case avlseg::intersection_op : return; // empty region case avlseg::difference_op : return; // empty region default : assert(false); } } if(region.Size()==0){ switch(op){ case avlseg::intersection_op: return; case avlseg::difference_op: result = line; return; default : assert(false); } } std::priority_queue, std::greater > q1; std::priority_queue, std::greater > q2; avltree::AVLTree sss; avlseg::ownertype owner; int pos1 = 0; int pos2 = 0; int size1= line.Size(); avlseg::ExtendedHalfSegment nextHs; int src = 0; avlseg::AVLSegment* member=0; avlseg::AVLSegment* leftN = 0; avlseg::AVLSegment* rightN = 0; avlseg::AVLSegment left1,right1,common1,left2,right2; int edgeno =0; avlseg::AVLSegment tmpL,tmpR; bool done = false; result.StartBulkLoad(); // perform a planesweeo while(((owner=selectNext(line,pos1,region,pos2,q1,q2,nextHs,src)) != avlseg::none) && ! done){ avlseg::AVLSegment current(nextHs,owner); member = sss.getMember(current,leftN,rightN); if(leftN){ tmpL = *leftN; leftN = &tmpL; } if(rightN){ tmpR = *rightN; rightN = &tmpR; } if(nextHs.IsLeftDomPoint()){ if(member){ // there is an overlapping segment in sss if(member->getOwner()==owner || member->getOwner()==avlseg::both ){ if(current.ininterior(member->getX2(),member->getY2())){ current.splitAtRight(member->getX2(),member->getY2(),right1); insertEvents(right1,true,true,q1,q2); } } else { // member and source come from difference sources uint32_t parts = member->split(current,left1,common1,right1); sss.remove(*member); member = &common1; if(parts & avlseg::LEFT){ if(!left1.isPoint()){ sss.insert(left1); insertEvents(left1,false,true,q1,q2); } } assert(parts & avlseg::COMMON); if(owner==avlseg::second) { // the region if(current.getInsideAbove()){ common1.con_above++; } else { common1.con_above--; } } // for a line is nothing to do if(!common1.isPoint()){ sss.insert(common1); insertEvents(common1,false,true,q1,q2); } if(parts & avlseg::RIGHT){ insertEvents(right1,true,true,q1,q2); } } } else { // no overlapping segment in sss found splitByNeighbour(sss,current,leftN,q1,q2, forceThrow); splitByNeighbour(sss,current,rightN,q1,q2, forceThrow); // update coverage numbers if(owner==avlseg::second){ // the region bool iac = current.getInsideAbove(); if(leftN && current.extends(*leftN)){ current.con_below = leftN->con_below; current.con_above = leftN->con_above; } else { if(leftN && leftN->isVertical()){ current.con_below = leftN->con_below; } else if(leftN){ current.con_below = leftN->con_above; } else { current.con_below = 0; } if(iac){ current.con_above = current.con_below+1; } else { current.con_above = current.con_below-1; } } } else { // the line if(leftN){ if(leftN->isVertical()){ current.con_below = leftN->con_below; } else { current.con_below = leftN->con_above; } } current.con_above = current.con_below; } // insert element if(!current.isPoint()){ sss.insert(current); insertEvents(current,false,true,q1,q2); } } } else { // nextHs.IsRightDomPoint() if(member && member->exactEqualsTo(current)){ switch(op){ case avlseg::intersection_op: { if( (member->getOwner()==avlseg::both) || (member->getOwner()==avlseg::first && member->con_above>0)){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } case avlseg::difference_op: { if( (member->getOwner()==avlseg::first) && (member->con_above==0)){ HalfSegment hs1 = member->convertToExtendedHs(true,avlseg::first); hs1.attr.edgeno = edgeno; result += hs1; hs1.SetLeftDomPoint(false); result += hs1; edgeno++; } break; } default : assert(false); } sss.remove(*member); splitNeighbours(sss,leftN,rightN,q1,q2,forceThrow); } if(pos1>=size1 && q1.empty()){ // line is processed done = true; } } } result.EndBulkLoad(); } #endif