http://opencv-extension-library.googlecode.com/svn/trunk/qtcvxlib/cvxcore/cvxcg.cpp
#include "cvxcg.h"
#include <assert.h>
#include <stdio.h>
//====================================================================
//====================================================================
// 判断某一与线段共线的点是否在线段上
static
bool icvxIsOnSegment(CvPoint pi, CvPoint pj, CvPoint pk)
{
int minx = (pi.x < pj.x)? pi.x: pj.x;
int maxx = (pi.x > pj.x)? pi.x: pj.x;
int miny = (pi.y < pj.y)? pi.y: pj.y;
int maxy = (pi.y > pj.y)? pi.y: pj.y;
// 点pk已经确定和线段直线共线
return pk.x >= minx && pk.x <= maxx
&& pk.y >= miny && pk.y <= maxy;
}
//====================================================================
//====================================================================
// 判断(pi->pj->pk)的方向
int cvxDirection(CvPoint pi, CvPoint pj, CvPoint pk)
{
int x1 = pk.x - pi.x;
int y1 = pk.y - pi.y;
int x2 = pj.x - pi.x;
int y2 = pj.y - pi.y;
return x1*y2 - x2*y1;
}
// 判断线段是否相交
bool cvxIsIntersect(CvPoint p1, CvPoint p2,
CvPoint p3, CvPoint p4,
CvPoint2D32f *px)
{
// 计算线段的相对位置
int d1 = cvxDirection(p3, p4, p1);
int d2 = cvxDirection(p3, p4, p2);
int d3 = cvxDirection(p1, p2, p3);
int d4 = cvxDirection(p1, p2, p4);
// 每个线段都跨越另一个线段的直线
if(((d1 > 0 && d2 < 0) || (d1 < 0 && d2 > 0))
&& ((d3 > 0 && d4 < 0) || (d3 < 0 && d4 > 0)))
{
if(px != NULL)
{
// 计算交点(没有验证)
int d = (p2.y-p1.y)*(p4.x-p3.x)-(p4.y-p3.y)*(p2.x-p1.x);
int tx = (p2.x-p1.x)*(p4.x-p3.x)*(p3.y-p1.y) +
(p2.y-p1.y)*(p4.x-p3.x)*p1.x -
(p4.y-p3.y)*(p2.x-p1.x)*p3.x;
int ty = (p2.y-p1.y)*(p4.y-p3.y)*(p3.x-p1.x) +
(p2.x-p1.x)*(p4.y-p3.y)*p1.y -
(p4.x-p3.x)*(p2.y-p1.y)*p3.y;
px->x = 1.f * tx/d;
px->y = -1.f * ty/d;
}
return true;
}
// 线段某个端点位于另一个线段上
if(d1 == 0 && icvxIsOnSegment(p3, p4, p1))
{
if(px != NULL) *px = cvPoint2D32f(p1.x, p1.y);
return true;
}
if(d2 == 0 && icvxIsOnSegment(p3, p4, p2))
{
if(px != NULL) *px = cvPoint2D32f(p2.x, p2.y);
return true;
}
if(d3 == 0 && icvxIsOnSegment(p1, p2, p3))
{
if(px != NULL) *px = cvPoint2D32f(p3.x, p3.y);
return true;
}
if(d4 == 0 && icvxIsOnSegment(p1, p2, p4))
{
if(px != NULL) *px = cvPoint2D32f(p4.x, p4.y);
return true;
}
// 不相交
return false;
}
//====================================================================
//====================================================================
// 判断多边形是否自交
bool cvxCheckPolygonSelfIntersect(const CvPoint *pts, int nPoints)
{
assert(pts != NULL);
assert(nPoints >= 3); // 多边形至少3个顶点
// 每条线段单独判断
for(int i = 0; i < nPoints; ++i)
{
int p1 = (i+0)%nPoints;
int p2 = (i+1)%nPoints;
for(int j = 0; j < nPoints; ++j)
{
int p3 = (j+0)%nPoints;
int p4 = (j+1)%nPoints;
// 两个线段不相邻
if(p3 == p1 || p3 == p2) continue;
if(p4 == p1 || p4 == p2) continue;
// 如果i和j对应的线段相交, 则返回
if(cvxIsIntersect(pts[p1], pts[p2], pts[p3], pts[p4]))
return true;
}
}
// 没有自交
return false;
}
// 判断多边形是否是顺时针方向
bool cvxCheckPolygonClockwise(const CvPoint *Pts, int nPoints)
{
assert(Pts != NULL);
assert(nPoints >= 3); // 多边形至少3个顶点
// 多边形不自交
assert(!cvxIsSelfIntersect(Pts, nPoints));
// 寻找一条所有顶点都在其一侧的边
for(int i = 0; i < nPoints; ++i)
{
int p1 = i;
int p2 = (i+1)%nPoints;
int px = (i+2)%nPoints;
// 第一个不相邻的点
int d = cvxDirection(Pts[p1], Pts[p2], Pts[px]);
// 确定方向
int flag = (d > 0);
// 判断剩余的点(除去第一个不相邻的点)
int j;
for(j = 0; j < nPoints; ++j)
{
int k = (px+j)%nPoints;
// p1, p2, px点除外
if(k == p1 || k == p2 || k == px) continue;
// 判断方向是否一致
int f = (cvxDirection(Pts[p1], Pts[p2], Pts[k]) > 0);
// 如果方向不同则终止
if(flag ^ f) break;
}
// 如果判断结束, 则返回结果
// 如果点始终在左边, 则为逆时针
if(j >= nPoints) return (!flag);
}
// 不可能执行到这里
assert(false);
return false;
}
//====================================================================
//
// 填充凹多边形算法:
//
// 每次剪去多边形的一个角,这样多边形的定点每次减少一个,直到最后
// 只剩下3个定点为止。在剪的时候,剪的位置不能和其他不相邻的边相交。
//
//====================================================================
void cvxFillConcavePoly(IplImage *img, CvPoint* pts, int npts, CvScalar color)
{
assert(CV_IS_IMAGE(img));
assert(pts != NULL && npts > 0);
// 多边形不能自相交
if(cvxIsSelfIntersect(pts, npts)) return;
// 备份多边形
CvPoint *poly = new CvPoint[npts];
memcpy(poly, pts, npts*sizeof(CvPoint));
// 多边形旋转方向
bool bClockwise = cvxIsClockwise(poly, npts);
// 依次处理每个凸出的定点
int cnt = 0;
while(true)
{
// 如果只剩3个点则直接填充
if(npts == 3)
{
cvFillConvexPoly(img, poly, 3, color);
break;
}
// 找可以被剪掉的顶点
int i, j, k;
for(i = 0; i < npts; ++i)
{
// i前后2个点的连线
int p1 = (i+npts-1)%npts;
int p2 = (i+npts+1)%npts;
// 对应的三角形
CvPoint pts_[3];
{
pts_[0] = poly[p1];
pts_[1] = poly[i];
pts_[2] = poly[p2];
}
// 旋转方向不能改变
if(bClockwise)
{
// 如果在i点不向右拐
// 则继续寻找
int d = cvxDirection(poly[p1], poly[i], poly[p2]);
if(d >= 0) continue;
}
else
{
// 如果在i点不向左拐
// 则继续寻找
int d = cvxDirection(poly[p1], poly[i], poly[p2]);
if(d <= 0) continue;
}
// 不和其他边相交
// 且不包含其他定点
// 则满足条件
for(j = 0; j < npts; ++j)
{
int p3 = (j+0)%npts;
int p4 = (j+1)%npts;
// 两个线段不相邻
if(p3 == i || p4 == i ) continue;
if(p3 == p1 || p3 == p2) continue;
// 其他顶点不在三角形内
if(cvxPtInTri(poly[p3], pts_)) break;
// 如果(p1,p2)和其他不相邻的边相交
// 也不满足条件
if(p4 == p1 || p4 == p2) continue;
if(cvxIsIntersect(poly[p1], poly[p2], poly[p3], poly[p4])) break;
}
// 如果j遍历了npts个点
// 说明已经找到满足条件的定点
// 注: 每个必然会找到一个合适的定点
if(j >= npts)
{
// i定点满足条件
// 先填充
cvFillConvexPoly(img, pts_, 3, color);
// 删除第i点
for(k = i; k < (npts-1); ++k)
{
poly[k] = poly[k+1];
}
npts -= 1;
// 继续循环
break;
}
}
}
delete[] poly;
return;
}
//====================================================================
//====================================================================
#define M_2PI (3.14159265*2)
// 比较float类型是否相等
static bool icvxFuzzyCompare(float p1, float p2)
{
return (fabs(p1 - p2) <= 0.00001f * MIN(fabs(p1), fabs(p2)));
}
// 获取线段的角度
float cvxGetLineAngle(float x1, float y1, float x2, float y2)
{
const float dx = x2 - x1;
const float dy = y2 - y1;
const float theta = (float)(atan2(-dy, dx) * 360.0 / M_2PI);
const float theta_normalized = theta < 0 ? theta + 360 : theta;
return icvxFuzzyCompare(theta_normalized, 360)?
0.f: theta_normalized;
}
// 设置线段的角度
// 线段长度和起点(p1,p2)不变, 新的终点为(p3,p4)
CvPoint cvxSetLineAngle(float x1, float y1, float x2, float y2, float angle)
{
const float angleR = (float)(angle * M_2PI / 360.0f);
const float l = (float)sqrt(((x1-x2)*(x1-x2)) + ((y1-y2)*(y1-y2)));
const float dx = (float)(cos(angleR) * l);
const float dy = (float)(-sin(angleR) * l);
x2 = x1 + dx;
y2 = y1 + dy;
return cvPoint(x2, y2);
}
// 旋转线段
// 线段长度和起点(p1,p2)不变, 新的终点为(p3,p4)
CvPoint cvxRotateLine(float x1, float y1, float x2, float y2, float angle)
{
float oldAngle = cvxGetLineAngle(x1, y1, x2, y2);
float newAngle = oldAngle + angle;
return cvxSetLineAngle(x1, y1, x2, y2, newAngle);
}
//====================================================================
//====================================================================
// 判断点是否在三角形内
int cvxPointTriangleTest(CvPoint tri[3], CvPoint pt)
{
assert(tri != NULL);
int d1 = cvxDirection(tri[0], tri[1], pt);
int d2 = cvxDirection(tri[1], tri[2], pt);
int d3 = cvxDirection(tri[2], tri[0], pt);
if((d1>0)&&(d2>0)&&(d3>0)) return 1;
if((d1<0)&&(d2<0)&&(d3<0)) return 1;
if((d1>=0)&&(d2>=0)&&(d3>=0)) return 0;
if((d1<=0)&&(d2<=0)&&(d3<=0)) return 0;
return -1;
}
//====================================================================
//====================================================================