// -*- c-basic-offset: 4 -*- /** @file Mask.h * * @brief declaration of classes to work with mask * * @author Thomas Modes * */ /* This program 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. * * This software 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 this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ // for debugging #include #include #include "Mask.h" #include #include //#include //#include #include namespace HuginBase { using namespace hugin_utils; bool MaskPolygon::isInside(const FDiff2D p) const { if(m_polygon.size()<3) return false; if(!m_boundingBox.contains(vigra::Point2D(p.x,p.y))) return false; int wind=getWindingNumber(p); if(m_invert) return wind==0; else return wind!=0; }; int MaskPolygon::getWindingNumber(const FDiff2D p) const { // algorithm is modified version of winding number method // described at http://www.softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm // Copyright 2001, softSurfer (www.softsurfer.com) // This code may be freely used and modified for any purpose // providing that this copyright notice is included with it. if(m_polygon.size()<3) return 0; int wind=0; FDiff2D a=m_polygon[m_polygon.size()-1]; for(unsigned int i=0;ip.y) if((b.x-a.x)*(p.y-a.y)<(p.x-a.x)*(b.y-a.y)) wind++; } else { if(b.y<=p.y) if((b.x-a.x)*(p.y-a.y)>(p.x-a.x)*(b.y-a.y)) wind--; }; a=b; }; return wind; }; int MaskPolygon::getTotalWindingNumber() const { if(m_polygon.size()<2) return 0; MaskPolygon diffPoly; unsigned int count=m_polygon.size(); for(unsigned int i=0;imax_distance) { //add intermediate points double currentDistance=max_distance; while(currentDistance0) { m_boundingBox.setUpperLeft(vigra::Point2D(m_polygon[0].x,m_polygon[0].y)); m_boundingBox.setLowerRight(vigra::Point2D(m_polygon[0].x+1,m_polygon[0].y+1)); if(m_polygon.size()>1) { for(unsigned int i=1;i=r.left(); case clipRight: return p.x<=r.right(); case clipTop: return p.y>=r.top(); case clipBottom: return p.y<=r.bottom(); }; //this should never happens return false; } FDiff2D clip_getIntersection(const FDiff2D p, const FDiff2D q, const vigra::Rect2D r, const clipSide side) { double a; double b; double xnew; double ynew; if(q.x-p.x==0) { a=0; b=p.y; } else { a=(q.y-p.y)/(q.x-p.x); b=p.y-p.x*a; }; switch(side){ case clipLeft: xnew=r.left(); ynew=xnew*a+b; break; case clipRight: xnew=r.right(); ynew=xnew*a+b; break; case clipTop: ynew=r.top(); if(a!=0) xnew=(ynew-b)/a; else xnew=p.x; break; case clipBottom: ynew=r.bottom(); if(a!=0) xnew=(ynew-b)/a; else xnew=p.x; break; }; return FDiff2D(xnew,ynew); }; VectorPolygon clip_onPlane(const VectorPolygon polygon, const vigra::Rect2D r, const clipSide side) { if(polygon.size()<3) { return polygon; }; FDiff2D s=polygon[polygon.size()-1]; FDiff2D p; VectorPolygon newPolygon; for(unsigned int i=0;i2); }; //helper function for clipping /** check if point is inside circle @returns true, if point p is inside circle given by center and radius @param p point to test @param center center point of circle test @param radius radius of circle to test */ bool clip_insideCircle(const FDiff2D p, const FDiff2D center, const double radius) { return p.squareDistance(center)<=radius*radius; }; /** returns intersection of line and circle @param p fist point of line segment @param s seconst point of line segment @param center center of circle @param radius radius of circle @returns vector with all intersection of line between p and s and given circle */ std::vector clip_getIntersectionCircle(const FDiff2D p, const FDiff2D s, const FDiff2D center, const double radius) { std::vector intersections; FDiff2D slope=s-p; if(slope.squareLength()<1e-5) { return intersections; }; FDiff2D p2=p-center; double dotproduct=p2.x*slope.x+p2.y*slope.y; double root=sqrt(dotproduct*dotproduct-slope.squareLength()*(p2.squareLength()-radius*radius)); double t1=(-dotproduct+root)/slope.squareLength(); double t2=(-dotproduct-root)/slope.squareLength(); std::set t; if(t1>0 && t1<1) { t.insert(t1); }; if(t2>0 && t2<1) { if(fabs(t2-t1)>1e-5) { t.insert(t2); }; }; if(t.size()>0) { for(std::set::const_iterator it=t.begin();it!=t.end();it++) { intersections.push_back(p+slope*(*it)); }; }; return intersections; }; /** calculates angle between vector a and b in radians */ double angle_between(const FDiff2D a, const FDiff2D b) { return asin((a.x*b.y-a.y*b.x)/(sqrt(a.squareLength())*sqrt(b.squareLength()))); }; /** adds an arc with given radius at the end of the polygon, the point is not added to the arc @param poly polygon to which the arc should added @param s point to which the arc should go @param center center of arc @param radius radius of arc @param clockwise true, if arc should go clockwise; else it goes anti-clockwise */ void generateArc(VectorPolygon& poly, const FDiff2D s, const FDiff2D center, const double radius, const bool clockwise) { if(poly.size()==0) { return; }; FDiff2D p=poly[poly.size()-1]; double maxDistance=5.0; if(p.squareDistance(s)(PI/180,atan2(maxDistance,radius)); if(!clockwise) { while(final_angleangle) { final_angle-=2*PI; }; angle-=step; while(angle>final_angle) { poly.push_back(FDiff2D(cos(angle)*radius+center.x,sin(angle)*radius+center.y)); angle-=step; }; }; }; bool MaskPolygon::clipPolygon(const FDiff2D center,const double radius) { if(radius<=0 || m_polygon.size()<3) { return false; }; FDiff2D s=m_polygon[m_polygon.size()-1]; bool s_inside=clip_insideCircle(s,center,radius); FDiff2D p; VectorPolygon newPolygon; bool needsFinalArc=false; double angleCovered=0; double angleCoveredOffset=0; for(unsigned int i=0;i points=clip_getIntersectionCircle(p,s,center,radius); DEBUG_ASSERT(points.size()==1); angleCovered+=angle_between(s-center,points[0]-center); if(newPolygon.size()==0) { needsFinalArc=true; angleCoveredOffset=angleCovered; } else { generateArc(newPolygon,points[0],center,radius,angleCovered<0); }; newPolygon.push_back(points[0]); newPolygon.push_back(p); }; } else { if(!s_inside) { //both points outside of circle std::vector points=clip_getIntersectionCircle(s,p,center,radius); //intersection can only be zero points or 2 points if(points.size()>1) { angleCovered+=angle_between(s-center,points[0]-center); if(newPolygon.size()==0) { needsFinalArc=true; angleCoveredOffset=angleCovered; } else { generateArc(newPolygon,points[0],center,radius,angleCovered<0); }; newPolygon.push_back(points[0]); newPolygon.push_back(points[1]); angleCovered=angle_between(points[1]-center,p-center); } else { angleCovered+=angle_between(s-center,p-center); }; } else { //line segment intersects circle from inside std::vector points=clip_getIntersectionCircle(s,p,center,radius); angleCovered=0; DEBUG_ASSERT(points.size()==1); newPolygon.push_back(points[0]); }; }; s=p; s_inside=p_inside; }; if(needsFinalArc && newPolygon.size()>1) { generateArc(newPolygon,newPolygon[0],center,radius,(angleCovered+angleCoveredOffset)<0); }; m_polygon=newPolygon; return (m_polygon.size()>2); }; void MaskPolygon::rotate90(bool clockwise,unsigned int maskWidth,unsigned int maskHeight) { for(unsigned int i=0;i=0.1) && (u<=0.9)) { // intersection is between p1 and p2 FDiff2D footpoint=p1+diff*u; // now check distance between intersection and p if(norm(p-footpoint)>x) if(is>>y) m_polygon.push_back(FDiff2D(x,y)); }; return m_polygon.size()>2; }; void MaskPolygon::printPolygonLine(std::ostream &o, const unsigned int newImgNr) const { o<<"k i"<