// -*- 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, see
* .
*
*/
// for debugging
#include
#include
#include "Mask.h"
#include
#include
#include
#include
namespace HuginBase {
bool MaskPolygon::isInside(const hugin_utils::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 hugin_utils::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;
hugin_utils::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(currentDistance1)
{
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;
}
hugin_utils::FDiff2D clip_getIntersection(const hugin_utils::FDiff2D p, const hugin_utils::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 hugin_utils::FDiff2D(xnew,ynew);
};
VectorPolygon clip_onPlane(const VectorPolygon& polygon, const vigra::Rect2D r, const clipSide side)
{
if(polygon.size()<3)
{
return polygon;
};
hugin_utils::FDiff2D s=polygon[polygon.size()-1];
hugin_utils::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 hugin_utils::FDiff2D p, const hugin_utils::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 hugin_utils::FDiff2D p, const hugin_utils::FDiff2D s, const hugin_utils::FDiff2D center, const double radius)
{
std::vector intersections;
hugin_utils::FDiff2D slope=s-p;
if(slope.squareLength()<1e-5)
{
return intersections;
};
hugin_utils::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.empty())
{
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 hugin_utils::FDiff2D a, const hugin_utils::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 hugin_utils::FDiff2D s, const hugin_utils::FDiff2D center, const double radius, const bool clockwise)
{
if(poly.empty())
{
return;
};
hugin_utils::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(hugin_utils::FDiff2D(cos(angle)*radius+center.x,sin(angle)*radius+center.y));
angle-=step;
};
};
};
bool MaskPolygon::clipPolygon(const hugin_utils::FDiff2D center,const double radius)
{
if(radius<=0 || m_polygon.size()<3)
{
return false;
};
hugin_utils::FDiff2D s=m_polygon[m_polygon.size()-1];
bool s_inside=clip_insideCircle(s,center,radius);
hugin_utils::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.empty())
{
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.empty())
{
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;
calcBoundingBox();
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
hugin_utils::FDiff2D footpoint=p1+diff*u;
// now check distance between intersection and p
if(norm(p-footpoint)> x)
{
double y;
if (is >> y)
{
m_polygon.push_back(hugin_utils::FDiff2D(x, y));
};
};
};
calcBoundingBox();
return m_polygon.size()>2;
};
void MaskPolygon::printPolygonLine(std::ostream &o, const unsigned int newImgNr) const
{
o<<"k i"<