class stVisibility { public: static void angularSort(const point &ptOrigin, const list & lstSeg, list & lstAngularSortedSeg); static void rect2Polar( segment & polarSeg, const segment & rectSeg); static void polar2Rect( segment & rSeg, const segment & pSeg); static void rect2Polar( point & polarPt, const point & rectPt); static void polar2Rect( point & rectPt,const point & polarPt); static int NearEndPoint(const segment &seg2, const point &ptInter); static void visibilityGaps(const point &ptOrigin, const list & lstVisSeg, list & lstGapSeg); static point closerPoint(const point & ptOrigin, const point & pt1 ,bool & pt1Closer, const point & pt2 , bool & pt2Closer); static int FrontBackTest(const point & ptOrigin, const segment & seg1 , bool & seg1Front, const segment & seg2 , bool & seg2Front); static void EndPointVisibility( const point &ptOrigin, const segment & segBack, bool & sourceVisible, bool &targetVisible, const segment &segFront); static void extractVisibleRegion(const point & ptOrigin, const segment & frontSeg, const segment & backSeg, list & lstExtractedSeg); static void HandleDisjointSegments(const point & ptOrigin, const segment & currVisSeg, const segment & newSeg, list & lstCurrSeg, list & lstNewSeg); static int HandleEndPtIntersection(const point & ptOrigin, const segment & currVisSeg, const segment & newSeg, list & lstCurrSeg, list & lstNewSeg); static void getOtherEndPoint(const segment &seg, const point & pt, point &otherPt); static void visiblePolygon(point ptOrigin, list lstSeg, list &lstVisSeg, float minLength = 0.15); /* Given the list of visible segments from the ptOrigin project all the segments into the projSeg and finds the parts visible on the projected plane */ static void projectVisiblity(point ptOrigin, list lstVisSeg, const segment & projSeg, list &lstProjSeg); }; void stVisibility :: angularSort(const point & ptOrigin, const list & lstSeg, list & lstAngularSortedSeg) { lstAngularSortedSeg.clear(); if(lstSeg.empty()) return; segment rectSeg; list lstPolarSeg; forall(rectSeg, lstSeg) { rectSeg = rectSeg.translate(-ptOrigin.xcoord(), -ptOrigin.ycoord()); segment polarSeg; rect2Polar(polarSeg , rectSeg); lstPolarSeg.push_back(polarSeg); } lstPolarSeg.sort( compare); segment drawSeg; forall(drawSeg, lstPolarSeg) { segment drawRectSeg; polar2Rect( drawRectSeg,drawSeg); drawRectSeg = drawRectSeg.translate(ptOrigin.xcoord(), ptOrigin.ycoord()); lstAngularSortedSeg.push_back(drawRectSeg); } } void stVisibility :: rect2Polar( point & polarPt, const point & rectPt) { double dist, angle; dist = rectPt.distance(); angle = atan2(rectPt.ycoord(), rectPt.xcoord()); if( angle < 0.0) angle = 6.28 + angle; polarPt = point(angle,dist); } void stVisibility :: polar2Rect( point & rectPt, const point & polarPt) { double dist = polarPt.ycoord(), angle = polarPt.xcoord(); rectPt = point(dist * cos(angle), dist * sin(angle)); } void stVisibility :: rect2Polar(segment &polarSeg, const segment & rSeg) { point pSource = point( rSeg.source().xcoord() , rSeg.source().ycoord()), pTarget = point(rSeg.target().xcoord() , rSeg.target().ycoord()); double dist[2] = { pSource.distance(), pTarget.distance()}; double angle[2] = {atan2(pSource.ycoord(), pSource.xcoord()), atan2(pTarget.ycoord(),pTarget.xcoord())}; if( angle[0] < 0.0) angle[0] = 6.28 + angle[0]; if( angle[1] < 0.0) angle[1] = 6.28 + angle[1]; /* SWAPPING to make angle[0] < angle[1] */ if(angle[0] >= angle[1]) { if( angle[0] > angle[1] || (angle[0] == angle[1] && dist[0] > dist[1])) { double tempAngle = angle[0], tempDist = dist[0]; angle[0] = angle[1]; dist[0] = dist[1]; angle[1] = tempAngle; dist[1] = tempDist; } } if(fabs(angle[1] - angle[0]) > 3.14) polarSeg = segment( point(angle[1],dist[1]), point(angle[0],dist[0])); else polarSeg = segment(point(angle[0],dist[0]), point(angle[1],dist[1])); } void stVisibility :: polar2Rect( segment & rSeg, const segment &pSeg) { point pSource = pSeg.source(); point pTarget = pSeg.target(); double angle[2] = { pSource.xcoord(), pTarget.xcoord()}; double dist[2] = { pSource.ycoord(), pTarget.ycoord()}; point rSource( dist[0] * cos(angle[0]), dist[0] * sin(angle[0])); point rTarget( dist[1] * cos(angle[1]), dist[1] * sin(angle[1])); rSeg = segment(rSource, rTarget); } void stVisibility :: visibilityGaps(const point &ptOrigin, const list & lstVisSeg, list & lstGapSeg) { lstGapSeg.clear(); segment currSeg, prevSeg; if(lstVisSeg.empty()) { cout<<"lstVisSeg.empty() is TRUE"; return; } prevSeg = lstVisSeg.tail(); forall(currSeg , lstVisSeg) { if(prevSeg.target().distance(currSeg.source()) > 0.1) { point polPrev, polCurr; double tx = ptOrigin.xcoord(),ty = ptOrigin.ycoord() ; point prevTar = prevSeg.target(); rect2Polar(polPrev, prevTar.translate(-tx,-ty) ); point currTar = currSeg.source(); rect2Polar(polCurr, currTar.translate(-tx,-ty) ); double angDiff = fabs( polPrev.xcoord() - polCurr.xcoord()); if( angDiff < 10.0 * 0.01746 || angDiff > 350.0 *0.01746 ) { point rectPrev, rectCurr; polar2Rect( rectPrev, polPrev); polar2Rect( rectCurr, polCurr); lstGapSeg.push_back(segment (rectPrev.translate(tx,ty),rectCurr.translate(tx,ty))); } } prevSeg = currSeg; } void stVisibility :: visiblePolygon(point ptOrigin,list lstSeg, list &lstVisSeg, float minLength ) { lstVisSeg.clear(); if(lstSeg.size() == 1)// Degenerate Case { lstVisSeg = lstSeg; return; } if(lstSeg.empty())// Safety Case return; /* my currentlist of visible segment = first segment and then i incrementally add segments from lstSeg and update the current set of visible segments*/ list lstCurrVisSeg; lstCurrVisSeg.push(lstSeg.pop()); segment newSeg; // Incremental addition of segments do { newSeg = lstSeg.pop(); list lstNextCurrVisSeg; /* checking with the currently visible segments*/ segment currVisSeg; list lstNewSeg; bool hidden = false;// indicates that new segment is not hidden forall(currVisSeg, lstCurrVisSeg) { if(newSeg.length()> minLength && currVisSeg.length() > minLength) { list lstCurrSeg; if(currVisSeg.contains(newSeg.source()) || currVisSeg.contains(newSeg.target())) { HandleEndPtIntersection(ptOrigin, currVisSeg, newSeg, lstCurrSeg, lstNewSeg); } else { HandleDisjointSegments( ptOrigin, currVisSeg, newSeg, lstCurrSeg, lstNewSeg); } lstNextCurrVisSeg.conc(lstCurrSeg); if(lstNewSeg.empty()) { hidden = true; break; } if(lstNewSeg.size() >= 2)// if the new segment is split in to two { segment otherNewSeg = lstNewSeg.pop(); if(otherNewSeg.length() > minLength) lstSeg.push(otherNewSeg); } newSeg = lstNewSeg.pop();// take the new segment after visiblity check } else { if(newSeg.length() <= minLength) { hidden = true; //cout<<"lstNewSeg is Empty \n"; break; } } if(newSeg.length() <= minLength) { hidden = true; break; } }//End of forall MACRO if(!hidden) { lstCurrVisSeg.clear(); if(newSeg.length() >= minLength) lstCurrVisSeg.push(newSeg); segment checkSeg; int check = 0; forall(checkSeg,lstNextCurrVisSeg) { if(checkSeg.length() > minLength) { lstCurrVisSeg.push(checkSeg); } } } else { segment checkSeg; int check = 0; lstNextCurrVisSeg =lstCurrVisSeg; lstCurrVisSeg.clear(); forall(checkSeg,lstNextCurrVisSeg) { if(checkSeg.length() > minLength) { lstCurrVisSeg.push(checkSeg); } } /* lstCurrVisSeg remains unchanged */ } } while(!lstSeg.empty()); segment filteredSeg; forall(filteredSeg,lstCurrVisSeg) { if(filteredSeg.length() > minLength) lstVisSeg.push_back(filteredSeg); } } int stVisibility :: NearEndPoint(const segment &seg2, const point &ptInter) { if( seg2.source().distance(ptInter) <= 0.10 || seg2.target().distance(ptInter) <= 0.10 ) return 1; else return 0; } /* PRE_CONDITION: seg1 and seg2 DO NOT intersect Result: return 1 and the below result seg1Front is true if its in front or else seg2Front is true as seen from "ptOrigin" if both are false it returns '0' and means they dont interfere */ int stVisibility :: FrontBackTest(const point & ptOrigin, const segment & seg1 , bool & seg1Front, const segment & seg2 , bool & seg2Front) { seg1Front = seg2Front = false; point ptInter; if(ray(ptOrigin,seg1.source()).intersection(seg2,ptInter) ) { closerPoint(ptOrigin,seg1.source(),seg1Front, ptInter, seg2Front); return 1; } if (ray(ptOrigin, seg1.target()).intersection(seg2,ptInter) ) { closerPoint(ptOrigin, seg1.target(), seg1Front, ptInter, seg2Front); return 1; } if( ray(ptOrigin, seg2.source()).intersection(seg1,ptInter) ) { closerPoint(ptOrigin,seg2.source(),seg2Front, ptInter,seg1Front); return 1; } if(ray(ptOrigin, seg2.target()).intersection(seg1,ptInter) ) { closerPoint(ptOrigin,seg2.target(),seg2Front, ptInter, seg1Front); return 1; } return 0; } /* Result: closer of pt1 and pt2 is returned pt1Closer is true if pt1 is closer or else pt2Closer is true if they both are equal the both are true Note: one of them has to be true , GEOMTRICAL FACT */ point stVisibility :: closerPoint(const point & ptOrigin, const point & pt1 , bool & pt1Closer, const point & pt2 , bool & pt2Closer) { if(pt1 == pt2) { pt1Closer = pt2Closer = true; return pt1; } if(ptOrigin.distance(pt1) <= ptOrigin.distance(pt2)) { pt1Closer = true ; pt2Closer = false; if(pt1 == pt2) pt1Closer = pt2Closer = true; return pt1; } else { pt1Closer = false; pt2Closer = true; return pt2; } } /* PRE-CONDITION: segBack is BEHIND segFront ,use FrontBackTest func before Result: sourceVisible = true if not occluded by segFront targetVisible = true if not occluded by segFront else they are false i.e not visible from "ptOrigin" */ void stVisibility :: EndPointVisibility( const point &ptOrigin, const segment & segBack, bool & sourceVisible, bool &targetVisible, const segment &segFront) { sourceVisible = targetVisible = true; point ptInter; bool interFront; if(ray(ptOrigin,segBack.source()).intersection(segFront,ptInter)) { if( NearEndPoint(segFront, ptInter)) { sourceVisible = false; } else { closerPoint(ptOrigin,ptInter,interFront, segBack.source(),sourceVisible); } } if(ray(ptOrigin,segBack.target()).intersection(segFront,ptInter)) { if( NearEndPoint(segFront, ptInter) ) { targetVisible = false; } else { closerPoint(ptOrigin,ptInter,interFront, segBack.target(),targetVisible); } } } /* PRE: currVisSeg is cuurently visible newSeg is added and checked for obstruction The two segments dont not intersect EVEN in the Endpoints Note: It works for SOME end point intersections but i feel it ptoduces segments with source and target point to be the SAME and In some cases it does not work Result: viewing from 'ptOrigin ' lstCurrSeg - contains the visible part(s) of currVisSeg lstNewSeg - contains the visible part(s) of newSeg */ void stVisibility :: HandleDisjointSegments(const point & ptOrigin, const segment & currVisSeg, const segment & newSeg, list & lstCurrSeg, list & lstNewSeg) { bool newSegFront, currVisSegFront; lstCurrSeg.clear(); lstNewSeg.clear(); int frontBack = FrontBackTest(ptOrigin,currVisSeg, currVisSegFront, newSeg,newSegFront); if(0 == frontBack ) { lstCurrSeg.push(currVisSeg); lstNewSeg.push(newSeg); return; } if(currVisSegFront) { lstCurrSeg.push(currVisSeg); extractVisibleRegion(ptOrigin,currVisSeg,newSeg, lstNewSeg); } else { if(newSegFront) { lstNewSeg.push(newSeg); extractVisibleRegion(ptOrigin,newSeg,currVisSeg, lstCurrSeg); } } } /* PRE: 1. frontSeg is in FRONT of backSeg 2. lstExtractedSeg is EMPTY Result: the visible part/ parts of backSeg from "ptOrigin " are in lstExtractedSeg */ void stVisibility :: extractVisibleRegion(const point & ptOrigin, const segment & frontSeg, const segment & backSeg, list & lstExtractedSeg) { bool sourceVisible,targetVisible; EndPointVisibility(ptOrigin, backSeg, sourceVisible,targetVisible, frontSeg); int visibilityCase = sourceVisible + targetVisible ; switch(visibilityCase) { case 2:// Both the End Points Are visible { point ptProjection[2]; bool raySourceHit , rayTargetHit; raySourceHit = ray(ptOrigin,frontSeg.source()).intersection(backSeg,ptProjection[0]); rayTargetHit = ray(ptOrigin,frontSeg.target()).intersection(backSeg,ptProjection[1]); int hitCase = raySourceHit + rayTargetHit; switch(hitCase) { case 2: { if(raySourceHit && rayTargetHit) { if(backSeg.source().distance(ptProjection[0]) < backSeg.source().distance(ptProjection[1])) { lstExtractedSeg.push(segment(backSeg.source(),ptProjection[0])); lstExtractedSeg.push(segment(backSeg.target(),ptProjection[1])); } else { lstExtractedSeg.push(segment(backSeg.source(),ptProjection[1])); lstExtractedSeg.push(segment(backSeg.target(),ptProjection[0])); } } break; } case 1: {// this happens due to numerical err so take the BIGGER SEGMENT // since only one ray projects point ptProjected; if(raySourceHit ) { ptProjected = ptProjection[0]; } else { ptProjected = ptProjection[1]; } segment projSeg1(backSeg.source(),ptProjected), projSeg2(backSeg.target(),ptProjected); point midPt1 = point ( (projSeg1.source().xcoord() + projSeg1.target().xcoord())/2.0, (projSeg1.source().ycoord() +projSeg1.target().ycoord())/ 2.0 ); point midPt2 = point ( (projSeg2.source().xcoord() + projSeg2.target().xcoord())/2.0, (projSeg2.source().ycoord() +projSeg2.target().ycoord())/ 2.0 ); if(segment(ptOrigin,midPt1).intersection(frontSeg)) { } else { lstExtractedSeg.push(projSeg1); } if(!segment(ptOrigin,midPt2).intersection(frontSeg)) { lstExtractedSeg.push(projSeg2); } break; } case 0: { lstExtractedSeg.push(backSeg); break; } }// end of INNER switch(hitcase); break; } case 1:// One of The End Points Are Visible { point ptProjection[2]; bool raySourceHit , rayTargetHit; ray rayOrignToFrontSource(ptOrigin,frontSeg.source()), rayOrignToFrontTarget(ptOrigin,frontSeg.target()); raySourceHit = rayOrignToFrontSource.intersection(backSeg,ptProjection[0]); rayTargetHit = rayOrignToFrontTarget.intersection(backSeg,ptProjection[1]); point backVisPt; if(sourceVisible) { backVisPt = backSeg.source(); } else { backVisPt = backSeg.target(); } int rayHitChoice = raySourceHit + rayTargetHit; switch(rayHitChoice) { case 1: { if(raySourceHit) { segment segProj(backVisPt,ptProjection[0]); point midPt ((segProj.source().xcoord() + backVisPt.xcoord())/2.0,(segProj.source().ycoord() + backVisPt.ycoord())/2.0); if(false == segment(ptOrigin,midPt).intersection(frontSeg)) lstExtractedSeg.push(segProj);//ment(backVisPt,ptProjection[0])); } else { segment segProj(backVisPt,ptProjection[1]); point midPt ((segProj.source().xcoord() + backVisPt.xcoord())/2.0,(segProj.source().ycoord() + backVisPt.ycoord())/2.0); if(false == segment(ptOrigin,midPt).intersection(frontSeg)) lstExtractedSeg.push(segProj); } break; } case 2:// both raySourceHit && rayTargetHit are true { if(backVisPt.distance(ptProjection[0]) < backVisPt.distance(ptProjection[1])) { lstExtractedSeg.push(segment(backVisPt,ptProjection[0])); } else { lstExtractedSeg.push(segment(backVisPt,ptProjection[1])); } break; } default: { ray rayBackVisible(ptOrigin,backVisPt); float rayDiff1 = fabs(rayBackVisible.direction() - rayOrignToFrontSource.direction()); float rayDiff2 = fabs(rayBackVisible.direction() - rayOrignToFrontTarget.direction()); if( rayDiff1 > 0.05 && rayDiff2 > 0.05) { lstExtractedSeg.push(backSeg); } else { point midPt( (backSeg.source().xcoord() + backSeg.target().xcoord())/2.0,(backSeg.source().ycoord() + backSeg.target().ycoord())/2.0); if(false == segment(ptOrigin,midPt).intersection(frontSeg)) { lstExtractedSeg.push(backSeg); } else { ; } } break; } }// End of switch(rayHitChoice) break; } case 0:// `NONE' of the End Points Are Visible { break; } } } /* PRE: currVisSeg is cuurently visible newSeg is added and checked for obstruction The two segments INTERSECT in the Endpoints Result: viewing from 'ptOrigin ' lstCurrSeg - contains the visible part(s) of currVisSeg lstNewSeg - contains the visible part(s) of newSeg retun 0: then they dont have End pt Intersection return 1: valid execution */ int stVisibility :: HandleEndPtIntersection(const point & ptOrigin, const segment & currVisSeg, const segment & newSeg, list & lstCurrSeg, list & lstNewSeg) { point ptInter, newOtherPoint, currOtherPoint; //This is better than intersection checking as it handles // parallel & perpendicular end pt Intersection if( currVisSeg.contains(newSeg.source())) { ptInter =newSeg.source(); } else { if( currVisSeg.contains(newSeg.target())) ptInter = newSeg.target(); else { return 0 ; } } getOtherEndPoint(currVisSeg, ptInter , currOtherPoint); getOtherEndPoint(newSeg , ptInter , newOtherPoint); bool newVisible = false , currVisible = false; bool newRayInterCurrSeg = false, currRayInterNewSeg = false; point ptOnNew,ptOnCurr; if(ray(ptOrigin , currOtherPoint).intersection(newSeg,ptOnNew) ==false) { currVisible = true; } else { currRayInterNewSeg = true; } if(ray(ptOrigin , newOtherPoint).intersection(currVisSeg, ptOnCurr) ==false) { newVisible = true; } else { newRayInterCurrSeg = true; } if(currVisible == true && newVisible == true) { segment segOriginTocurrOtherPoint(ptOrigin,currOtherPoint), segOriginToNewOtherPoint(ptOrigin,newOtherPoint); float minAngle = 0.05; /* This removes a degenrate case when the other end points of the segments lie on the same ray from ptOrigin */ float directionDifference = segOriginTocurrOtherPoint.direction() - segOriginToNewOtherPoint.direction(); if(fabs(directionDifference) > minAngle ) { lstCurrSeg.push(currVisSeg); lstNewSeg.push(newSeg); } else { /* which ever end point is closer is OBVIOUSLY visible and the farther point is occluded */ if(ptOrigin.distance(currOtherPoint) < ptOrigin.distance(newOtherPoint)) { lstCurrSeg.push(currVisSeg); } else { lstNewSeg.push(newSeg); } } return 1; } if(currRayInterNewSeg) { point ptDummy; if(segment(ptOrigin , currOtherPoint).intersection(newSeg, ptDummy)) { // then new seg is in front and curr seg hidden lstNewSeg.push(newSeg); } else { // then new seg is hidden or partial and curr seg is front if(ptOrigin.distance(currOtherPoint) < ptOrigin.distance(newOtherPoint)) { lstCurrSeg.push(currVisSeg); segment partialNew(ptOnNew,newOtherPoint); if(partialNew.length() > 0.1)// to remove degenerate segments { lstNewSeg.push(partialNew); } else { ; } } else { lstNewSeg.push(newSeg); } } } else { if(newRayInterCurrSeg) { point ptDummy; if(segment(ptOrigin,newOtherPoint).intersection(currVisSeg, ptDummy)) { //the curr VisSeg is in front and new Seg is hidden lstCurrSeg.push(currVisSeg); } else { // curr VisSeg is hidden or partial vis and newSeg is front lstNewSeg.push(newSeg); segment partialCurr(ptOnCurr,currOtherPoint); if(partialCurr.length() > 0.1)//to remove degenerate segments lstCurrSeg.push(partialCurr); } } } return 1; } void stVisibility :: getOtherEndPoint(const segment &seg, const point & pt, point &otherPt) { if(seg.source() != pt) { otherPt = seg.source(); } else { if(seg.target() != pt) otherPt = seg.target(); else { cout<<"Numerical Error in getOtherPoint \n"; exit(0); } } } void stVisibility :: projectVisiblity(point ptOrigin, list lstVisSeg, const segment & projSeg, list &lstProjSeg) { float const minLen = 0.50; lstProjSeg.clear(); if(lstVisSeg.empty()) { lstProjSeg.push(projSeg); return; } list lstUFProjSeg;// list of un-filtered segments lstUFProjSeg.push(projSeg); segment visSeg; forall(visSeg, lstVisSeg) { list lstTempProjSeg; segment UFprojSeg; forall(UFprojSeg, lstUFProjSeg) { list lstExtractedSeg; extractVisibleRegion( ptOrigin, visSeg, UFprojSeg , lstExtractedSeg); lstTempProjSeg.conc(lstExtractedSeg); } lstUFProjSeg.clear(); segment filterSeg; forall(filterSeg,lstTempProjSeg) { if(filterSeg.length() > minLen) lstUFProjSeg.push(filterSeg); } } segment filterSeg; forall(filterSeg,lstUFProjSeg) { if(filterSeg.length() > minLen) lstProjSeg.push(filterSeg); } }