VC++ 平面坐标中点集的判断

之前做雷达扫射图的时候用到的一个接口类,本接口实现以下功能:

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 &center);

	// 线段相交-排斥实验
	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 &center)
{
	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)
{
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值