之前做雷达扫射图的时候用到的一个接口类,本接口实现以下功能:
1、已知点P(x,y)和多边形Poly,判断点P(x,y)是否在多边形内部。
bool IsPointInPolygon(std::vector<POINT> poly,POINT pt);
2、已知两条线段P1P2和Q1Q2,判断P1P2和Q1Q2是否相交,若相交,求出交点
bool GetCrossPOINT(const POINT &p1,const POINT &p2,const POINT &q1,const POINT &q2,long &x,long &y);
3、多边形交集-已知两个多边形Poly1和Poly2,分别由点集C1={P1,P2,...,Pm}和C2={Q1,Q2,...,Qn}表示,求这两个多边形的交集。
bool PolygonClip(const vector<POINT> &poly1,const vector<POINT> &poly2, std::vector<POINT> &interPoly);
4、判断点在圆弧内
bool IsPointInAngleArc(const POINT &pCenter, const float nRadius, const int nRadian, const int nAngle, const POINT &pPoint);
头文件声明(MathFormula.h):
#pragma once
#include <vector>
using namespace std;
#define PI 3.1415926
inline int sgn(float d)
{
if(d<0) return -1;
else if (d==0) return 0;
else return 1;
}
class CMathFormula
{
public:
CMathFormula(void);
~CMathFormula(void);
private:
// 多边形点集排序-若点a大于点b,即点a在点b顺时针方向,返回true,否则返回false
bool POINTCmp(const POINT &a,const POINT &b,const POINT ¢er);
// 线段相交-排斥实验
bool IsRectCross(const POINT &p1,const POINT &p2,const POINT &q1,const POINT &q2);
// 线段相交-跨立判断
bool IsLineSegmentCross(const POINT &pFirst1,const POINT &pFirst2,const POINT &pSecond1,const POINT &pSecond2);
protected:
// 多边形点集排序
// 已知多边形点集C={P1,P2,...,PN},其排列顺序是杂乱,依次连接这N个点,无法形成确定的多边形,需要对点集C进行排序后,再绘制多边形。
void ClockwiseSortPOINTs(std::vector<POINT> &vPOINTs);
public:
// 点在多边形内部
// 已知点P(x,y)和多边形Poly,判断点P(x,y)是否在多边形内部。
bool IsPointInPolygon(std::vector<POINT> poly,POINT pt);
// 线段相交
// 已知两条线段P1P2和Q1Q2,判断P1P2和Q1Q2是否相交,若相交,求出交点
bool GetCrossPOINT(const POINT &p1,const POINT &p2,const POINT &q1,const POINT &q2,long &x,long &y);
// 多边形交集-已知两个多边形Poly1和Poly2,分别由点集C1={P1,P2,...,Pm}和C2={Q1,Q2,...,Qn}表示,求这两个多边形的交集。
bool PolygonClip(const vector<POINT> &poly1,const vector<POINT> &poly2, std::vector<POINT> &interPoly);
// 判断点在圆弧内
bool IsPointInAngleArc(const POINT &pCenter, const float nRadius, const int nRadian, const int nAngle, const POINT &pPoint);
};
源码实现(MathFormula.cpp):
#include "stdafx.h"
#include "MathFormula.h"
/*
引用:http://www.cnblogs.com/dwdxdy/p/3230485.html
问题描述:已知两条线段P1P2和Q1Q2,判断P1P2和Q1Q2是否相交,若相交,求出交点。
两条线段的位置关系可以分为三类:有重合部分、无重合部分但有交点、无交点。
算法的步骤如下:
1.快速排斥实验。
设以线段P1P2为对角线的矩形为R,设以线段Q1Q2为对角线的矩形为T,如果R和T不相交,则两线段不相交。
2.跨立实验。
如果两线段相交,则两线段必然相互跨立对方。
若P1P2跨立Q1Q2,则矢量(P1-Q1)和(P2-Q1)位于矢量(Q2-Q1)的两侧,即( P1 - Q1 ) × ( Q2 - Q1 ) * ( P2 - Q1 ) × ( Q2 - Q1 ) < 0。
若Q1Q2跨立P1P2,则矢量(Q1-P1)和(Q2-P1)位于矢量(P2-P1)的两侧,即( Q1 - P1 ) × ( P2 - P1 ) * ( Q2 - P1 ) × ( P2 - P1 ) < 0。
*/
//排斥实验
bool CMathFormula::IsRectCross(const POINT &p1,const POINT &p2,const POINT &q1,const POINT &q2)
{
bool ret = min(p1.x,p2.x) <= max(q1.x,q2.x) &&
min(q1.x,q2.x) <= max(p1.x,p2.x) &&
min(p1.y,p2.y) <= max(q1.y,q2.y) &&
min(q1.y,q2.y) <= max(p1.y,p2.y);
return ret;
}
//跨立判断
bool CMathFormula::IsLineSegmentCross(const POINT &pFirst1,const POINT &pFirst2,const POINT &pSecond1,const POINT &pSecond2)
{
long line1,line2;
line1 = pFirst1.x * (pSecond1.y - pFirst2.y) +
pFirst2.x * (pFirst1.y - pSecond1.y) +
pSecond1.x * (pFirst2.y - pFirst1.y);
line2 = pFirst1.x * (pSecond2.y - pFirst2.y) +
pFirst2.x * (pFirst1.y - pSecond2.y) +
pSecond2.x * (pFirst2.y - pFirst1.y);
if (((line1 ^ line2) >= 0) && !(line1 == 0 && line2 == 0))
return false;
line1 = pSecond1.x * (pFirst1.y - pSecond2.y) +
pSecond2.x * (pSecond1.y - pFirst1.y) +
pFirst1.x * (pSecond2.y - pSecond1.y);
line2 = pSecond1.x * (pFirst2.y - pSecond2.y) +
pSecond2.x * (pSecond1.y - pFirst2.y) +
pFirst2.x * (pSecond2.y - pSecond1.y);
if (((line1 ^ line2) >= 0) && !(line1 == 0 && line2 == 0))
return false;
return true;
}
bool CMathFormula::GetCrossPOINT(const POINT &p1,const POINT &p2,const POINT &q1,const POINT &q2,long &x,long &y)
{
if(IsRectCross(p1,p2,q1,q2))
{
if (IsLineSegmentCross(p1,p2,q1,q2))
{
//求交点
long tmpLeft,tmpRight;
tmpLeft = (q2.x - q1.x) * (p1.y - p2.y) - (p2.x - p1.x) * (q1.y - q2.y);
tmpRight = (p1.y - q1.y) * (p2.x - p1.x) * (q2.x - q1.x) + q1.x * (q2.y - q1.y) * (p2.x - p1.x) - p1.x * (p2.y - p1.y) * (q2.x - q1.x);
x = (int)((double)tmpRight/(double)tmpLeft);
tmpLeft = (p1.x - p2.x) * (q2.y - q1.y) - (p2.y - p1.y) * (q1.x - q2.x);
tmpRight = p2.y * (p1.x - p2.x) * (q2.y - q1.y) + (q2.x- p2.x) * (q2.y - q1.y) * (p1.y - p2.y) - q2.y * (q1.x - q2.x) * (p2.y - p1.y);
y = (int)((double)tmpRight/(double)tmpLeft);
return true;
}
}
return false;
}
/*
引用:http://www.cnblogs.com/dwdxdy/p/3230647.html
问题描述:已知点P(x,y)和多边形Poly,判断点P(x,y)是否在多边形内部。
基本方法:射线法
以点P为端点,向左方作射线L,由于多边形是有界的,所以射线L的左端一定在多边形外部,考虑沿着L从无究远处开始自左向右移动。
遇到和多边形的第一个交点的时候,进入到了多边形的内部,遇到第二个交点的时候,离开了多边形...
因而当L和多边形的交点数目C是奇数的时候,P在多边形内,是偶数,则P在多边形外。
特殊情况分析,如图下图(a),(b),(c),(d)所示。
图(a)中,L和多边形的顶点相交,交点只能计算一个。
图(b)中,L和多边形顶点的交点不应被计算。
图(c)和(d)中,L和多边形的一条边重合,这条边应该被忽略不计。
*/
// The function will return YES if the POINT x,y is inside the polygon, or
// NO if it is not. If the POINT is exactly on the edge of the polygon,
// then the function may return YES or NO.
bool CMathFormula::IsPointInPolygon(std::vector<POINT> poly,POINT pt)
{
int i,j;
bool c = false;
for (i = 0,j = poly.size() - 1;i < poly.size();j = i++)
{
if ((((poly[i].y <= pt.y) && (pt.y < poly[j].y)) ||
((poly[j].y <= pt.y) && (pt.y < poly[i].y)))
&& (pt.x < (poly[j].x - poly[i].x) * (pt.y - poly[i].y)/(poly[j].y - poly[i].y) + poly[i].x))
{
c = !c;
}
}
return c;
}
/*
问题描述:已知多边形点集C={P1,P2,...,PN},其排列顺序是杂乱,依次连接这N个点,无法形成确定的多边形,需要对点集C进行排序后,再绘制多边形。
点集排序过程中,关键在于如何定义点的大小关系。
以按逆时针排序为例,算法步骤如下:
定义:点A在点B的逆时针方向,则点A大于点B
1.计算点集的重心O,以重心作为逆时针旋转的中心点。
2.计算点之间的大小关系。
大小关系的计算,可由两种方法进行计算。
方法1:
以重心O作一条平行于X轴的单位向量OX,然后依次计算OPi和OX的夹角,根据夹角的大小,确定点之间的大小关系。
OPi和OX夹角越大,说明点Pi越小。
方法2:
根据向量叉积的定义,向量OPi和OPj的叉积大于0,则向量OPj在向量OPi的逆时针方向,即点Pj小于点Pi。
*/
//若点a大于点b,即点a在点b顺时针方向,返回true,否则返回false
bool CMathFormula::POINTCmp(const POINT &a,const POINT &b,const POINT ¢er)
{
if (a.x >= 0 && b.x < 0)
return true;
if (a.x == 0 && b.x == 0)
return a.y > b.y;
//向量OA和向量OB的叉积
int det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y);
if (det < 0)
return true;
if (det > 0)
return false;
//向量OA和向量OB共线,以距离判断大小
int d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y);
int d2 = (b.x - center.x) * (b.x - center.y) + (b.y - center.y) * (b.y - center.y);
return d1 > d2;
}
void CMathFormula::ClockwiseSortPOINTs(std::vector<POINT> &vPOINTs)
{
//计算重心
POINT center;
double x = 0,y = 0;
for (int i = 0;i < vPOINTs.size();i++)
{
x += vPOINTs[i].x;
y += vPOINTs[i].y;
}
center.x = (int)x/vPOINTs.size();
center.y = (int)y/vPOINTs.size();
//冒泡排序
for(int i = 0;i < vPOINTs.size() - 1;i++)
{
for (int j = 0;j < vPOINTs.size() - i - 1;j++)
{
if (POINTCmp(vPOINTs[j],vPOINTs[j+1],center))
{
POINT tmp = vPOINTs[j];
vPOINTs[j] = vPOINTs[j + 1];
vPOINTs[j + 1] = tmp;
}
}
}
}
/*
引用:http://www.cnblogs.com/dwdxdy/archive/2013/08/02/3232110.html
问题描述:已知两个多边形Poly1和Poly2,分别由点集C1={P1,P2,...,Pm}和C2={Q1,Q2,...,Qn}表示,求这两个多边形的交集。
算法思想:
两个多边形相交后,其顶点要么是两个多边形边的交点,要么是在多边形内部的点。
算法步骤:
1.计算两个多边形每条边之间的交点。
2.计算包含在多边形内部的点。
3.将交点和多边形内部的点,按逆时针(或顺时针)排序,得出最终的点集。
*/
bool CMathFormula::PolygonClip(const vector<POINT> &poly1,const vector<POINT> &poly2, std::vector<POINT> &interPoly)
{
if (poly1.size() < 3 || poly2.size() < 3)
{
return false;
}
long x,y;
//计算多边形交点
for (int i = 0;i < poly1.size();i++)
{
int poly1_next_idx = (i + 1) % poly1.size();
for (int j = 0;j < poly2.size();j++)
{
int poly2_next_idx = (j + 1) % poly2.size();
if (GetCrossPOINT(poly1[i],poly1[poly1_next_idx],
poly2[j],poly2[poly2_next_idx],
x,y))
{
POINT pt;
pt.x = x;
pt.y = y;
interPoly.push_back(pt);
}
}
}
//计算多边形内部点
for(int i = 0;i < poly1.size();i++)
{
if (IsPointInPolygon(poly2,poly1[i]))
{
interPoly.push_back(poly1[i]);
}
}
for (int i = 0;i < poly2.size();i++)
{
if (IsPointInPolygon(poly1,poly2[i]))
{
interPoly.push_back(poly2[i]);
}
}
if(interPoly.size() <= 0)
return false;
//点集排序
ClockwiseSortPOINTs(interPoly);
return true;
}
// 判断点在圆弧内
/*
pCenter-圆心
nRadius-半径
Radian-弧度
pPoint-点坐标
*/
/*
/
/ |
C / | B
/ |
/ |
------
A
△ABC中,∠C=90度,A,B,C的对边分别为a,b,c,那么有以下等式: a²+b²=c²(勾股定理)
sinA=cosB=a/c cosA=sinB=b/c tanA=cotB=a/b cotA=tanB=b/a sinA²+cosA²=1 tanA×cotA=1
tanB=b/a tanA=a/b
∠A=atan(a/b) ∠B=atan(b/a)
函数里面的是弧度
弧度->角度 弧度*180/PI
角度->弧度 角度*PI/180
*/
bool CMathFormula::IsPointInAngleArc(const POINT &pCenter, const float nRadius, const int nRadian, const int nAngle, const POINT &pPoint)
{
float x = pPoint.x - pCenter.x;
float y = pCenter.y - pPoint.y;
float a = abs((float)(x));
float b = abs((float)(y));
float fDistance = sqrt((float)(pow((float)a, 2)) + (float)(pow((float)b, 2)));
if (fDistance > nRadius)
return false; // 距离大于半径
// 求∠B大小,与fRadian进行比较
float fV = b/a;
float fArcB = atan(fV) * 180 / PI;
if (0 == sgn(x))
{
if (0 == sgn(y))
fArcB = 0;
else if (sgn(y) > 0)
fArcB = 90;
else
fArcB = 270;
}
else if (0 == sgn(y))
{
if (sgn(x) < 0)
fArcB = 180;
else
fArcB = 0;
}
else if (sgn(x)>0 && sgn(y)>0) // 1象限
{
fArcB = atan(fV) * 180 / PI;
}
else if (sgn(x)<0 && sgn(y)>0) // 2象限
{
fArcB = 180 - atan(fV) * 180 / PI;
}
else if (sgn(x)<0 && sgn(y)<0) // 3象限
{
fArcB = 180 + atan(fV) * 180 / PI;
}
else if (sgn(x)>0 && sgn(y)<0) // 4象限
{
fArcB = 360 - atan(fV) * 180 / PI;
}
float fMinAngleArc = nAngle - nRadian/2;
float fMaxAngleArc = nAngle + nRadian/2;
if (fMinAngleArc>=0)
{
if (fArcB>fMaxAngleArc || fArcB<fMinAngleArc)
return FALSE;
}
else
{
if (fArcB>fMaxAngleArc && fArcB<(360+fMinAngleArc))
return FALSE;
}
return true;
}
CMathFormula::CMathFormula(void)
{
}
CMathFormula::~CMathFormula(void)
{
}