objectArx實現:點與閉合多段線的位置關係

點與閉合多段線的位置關係

射線法:從給定點出發,沿着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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章