判断线段相交 opencv-ext

判断线段相交 opencv-ext
2010-07-18 17:15
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;
}

//====================================================================
//====================================================================

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值