ObjectARX_acedGrRead/acedDragGen动态拖动

点与闭合多段线的位置关系

射线法:从给定点出发,沿着X轴正方向或者负方向做一条射线(射线可能跟多边形没有交点),计算射线跟多边形的交点数量,如果是奇数个交点,在内部;偶数个交点在外部。处理下点就在多边形的顶点上的特例。

--->参考ObjectARX(VC)开发基础与实例教程P254

	//************************************
	// Author:    WangHongFeng
	// Summary:   点与闭合多段线的位置关系
	// Method:    PtRelationToPoly
	// Access:    public 
	// Returns:   int  -1表示在多段线外部,0表示在多段线上,1表示在多段线内部
	// Parameter: AcDbPolyline * pPoly
	// Parameter: const AcGePoint2d & pt
	// Parameter: double tol
	//************************************
	int PtRelationToPoly(AcDbPolyline *pPoly, const AcGePoint2d &pt, double tol = 1.0E-7);

	bool PointIsPolyVert(AcDbPolyline *pPoly, const AcGePoint2d &pt, double tol);
	void IntersectWithGeRay(AcDbPolyline *pPoly, const AcGeRay2d &geRay, AcGePoint3dArray &intPoints, double tol = 1.0E-7);
	void FilterEqualPoints(AcGePoint3dArray &points, double tol = 1.0E-7);
	void FilterEqualPoints(AcGePoint3dArray &points, const AcGePoint2d &pt, double tol = 1.0E-7);
	int FindPoint(const AcGePoint2dArray &points, const AcGePoint2d &point, double tol = 1.0E-7);
int PubFuc::PtRelationToPoly(AcDbPolyline *pPoly, const AcGePoint2d &pt, double tol /*= 1.0E-7*/)
{
	assert(pPoly);

	// 1.如果点到多段线的最近点和给定的点重合,表示点在多段线上
	AcGePoint3d closestPoint;
	pPoly->getClosestPointTo(ToPoint3d(pt, pPoly->elevation()), closestPoint);		// 多段线上与给定点距离最近的点	
	if (fabs(closestPoint.x - pt.x) < tol && fabs(closestPoint.y - pt.y) < tol)			// 点在多段线上
	{
		return 0;
	}

	// 2.第一个射线的方向是从最近点到当前点,起点是当前点
	// 射线的起点是pt,方向为从最近点到pt,如果反向做判断,则最近点距离pt太近的时候,最近点也会被作为一个交点(这个交点不太容易被排除掉)
	// 此外,这样的射线方向很容易判断出点不在内部的情况
	AcGeVector3d vec(-(closestPoint[X] - pt[X]), -(closestPoint[Y] - pt[Y]), 0);
	AcGeRay2d geRay(AcGePoint2d(pt.x, pt.y), AcGePoint2d(pt.x + vec.x, pt.y + vec.y));

	// 3.射线与多段线计算交点
	AcGePoint3dArray intPoints;
	IntersectWithGeRay(pPoly, geRay, intPoints, 1.0E-4);
	// IntersectWith函数经常会得到很近的交点,这些点必须进行过滤
	PubFuc::FilterEqualPoints(intPoints, 1.0E-4);

	// 4.判断点和多段线的位置关系
RETRY:
	// 4.1 如果射线和多段线没有交点,表示点在多段线的外部
	if (intPoints.length() == 0)
	{
		return -1;
	}
	else
	{
		// 3.1 过滤掉由于射线被反向延长带来的影响
		PubFuc::FilterEqualPoints(intPoints, ToPoint2d(closestPoint));		// 2008-0907修订记录:当pt距离最近点比较近的时候,最近点竟然被作为一个交点!
																								// 3.2 如果某个交点与最近点在给定点的同一方向,要去掉这个点(这个点明显不是交点,还是由于intersectwith函数的Bug)	
		for (int i = intPoints.length() - 1; i >= 0; i--)
		{
			if ((intPoints[i][X] - pt[X]) * (closestPoint[X] - pt[X]) >= 0 &&
				(intPoints[i][Y] - pt[Y]) * (closestPoint[Y] - pt[Y]) >= 0)
			{
				intPoints.removeAt(i);
			}
		}

		int count = intPoints.length();
		int i;
		for (i = 0; i < intPoints.length(); i++)
		{
			if (PointIsPolyVert(pPoly, ToPoint2d(intPoints[i]), 1.0E-4))		// 只要有交点是多段线的顶点就重新进行判断
			{
				// 处理给定点很靠近多段线顶点的情况(如果与顶点距离很近,就认为这个点在多段线上,因为这种情况没有什么好的判断方法)
				if (PointIsPolyVert(pPoly, AcGePoint2d(pt.x, pt.y), 1.0E-4))
				{
					return 0;
				}

				// 将射线旋转一个极小的角度(2度)再次判断(假定这样不会再通过上次判断到的顶点)
				vec = vec.rotateBy(0.035, AcGeVector3d::kZAxis);
				geRay.set(AcGePoint2d(pt.x, pt.y), AcGePoint2d(pt.x + vec.x, pt.y + vec.y));
				intPoints.setLogicalLength(0);
				IntersectWithGeRay(pPoly, geRay, intPoints, 1.0E-4);
				goto RETRY;		// 继续判断结果
			}
		}

		if (count % 2 == 0)
		{
			return -1;
		}
		else
		{
			return 1;
		}
	}
}

bool PubFuc::PointIsPolyVert(AcDbPolyline *pPoly, const AcGePoint2d &pt, double tol)
{
	for (int i = 0; i < (int)pPoly->numVerts(); i++)
	{
		AcGePoint3d vert;
		pPoly->getPointAt(i, vert);

		AcGeTol gtol;
		gtol.setEqualPoint(tol);
		if (ToPoint2d(vert).isEqualTo(pt, gtol))
		{
			return true;
		}
	}

	return false;
}


// 几何类射线和多段线计算交点
void PubFuc::IntersectWithGeRay(AcDbPolyline *pPoly, const AcGeRay2d &geRay, AcGePoint3dArray &intPoints, double tol /*= 1.0E-7*/)
{
	intPoints.setLogicalLength(0);
	AcGePoint2dArray intPoints2d;

	// 多段线的每一段分别与射线计算交点
	AcGeTol geTol;
	geTol.setEqualPoint(tol);
	for (int i = 0; i < pPoly->numVerts(); i++)
	{
		if (i < pPoly->numVerts() - 1 || pPoly->isClosed() == Adesk::kTrue)
		{
			double bulge = 0;
			pPoly->getBulgeAt(i, bulge);
			if (fabs(bulge) < 1.0E-7)
			{
				// 构建几何类的线段来计算交点
				AcGeLineSeg2d geLine;
				Acad::ErrorStatus es = pPoly->getLineSegAt(i, geLine);
				AcGePoint2d intPoint;
				if (geLine.intersectWith(geRay, intPoint, geTol) == Adesk::kTrue)
				{
					if (PubFuc::FindPoint(intPoints2d, intPoint, tol) < 0)
					{
						intPoints2d.append(intPoint);
					}
				}
			}
			else
			{
				// 构建几何类的圆弧来计算交点
				AcGeCircArc2d geArc;
				pPoly->getArcSegAt(i, geArc);
				AcGePoint2d pt1, pt2;
				int count = 0;
				if (geArc.intersectWith(geRay, count, pt1, pt2, geTol) == Adesk::kTrue)
				{
					if (PubFuc::FindPoint(intPoints2d, pt1, tol) < 0)
					{
						intPoints2d.append(pt1);
					}
					if (count > 1 && PubFuc::FindPoint(intPoints2d, pt2, tol) < 0)
					{
						intPoints2d.append(pt2);
					}
				}
			}
		}
	}

	double z = pPoly->elevation();
	int i;
	for (i = 0; i < intPoints2d.length(); i++)
	{
		intPoints.append(AcGePoint3d(intPoints2d[i].x, intPoints2d[i].y, z));
	}
}

void PubFuc::FilterEqualPoints(AcGePoint3dArray &points, double tol /*= 1.0E-7*/)
{
	for (int i = points.length() - 1; i > 0; i--)
	{
		for (int j = 0; j < i; j++)
		{
			if (MathUtils::IsEqual(points[i].x, points[j].x, tol) && MathUtils::IsEqual(points[i].y, points[j].y, tol))
			{
				points.removeAt(i);
				break;
			}
		}
	}
}

// 在数组中查找某个点,返回点在数组中的索引,未找到则返回-1
int PubFuc::FindPoint(const AcGePoint2dArray &points, const AcGePoint2d &point, double tol /*= 1.0E-7*/)
{
	for (int i = 0; i < points.length(); i++)
	{
		AcGeTol gtol;
		gtol.setEqualPoint(tol);
		if (points[i].isEqualTo(point, gtol))
		{
			return i;
		}
	}

	return -1;
}

// 从数组中过滤掉重复点
void PubFuc::FilterEqualPoints(AcGePoint3dArray &points, const AcGePoint2d &pt, double tol /*= 1.0E-7*/)
{
	AcGePoint3dArray tempPoints;
	for (int i = 0; i < points.length(); i++)
	{
		if (ToPoint2d(points[i]).distanceTo(pt) > tol)
		{
			tempPoints.append(points[i]);
		}
	}

	points = tempPoints;
}
posted @   Wang_h_f  阅读(244)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示