ObjectARX射线法判断点和闭合多段线关系

objectArx实现:点与闭合多段线的位置关系_objectarx-CSDN专栏

ObjectARX如何判断点和多段线的关系_arx 多段线求交集-CSDN博客

objectArx实现:点与闭合多段线的位置关系-CSDN博客

具体原理流程大家可以看这几位大姥的原文,本人这里只给你总结下代码实现,编辑器是vs2017,

建议直接做一个方法类存放该方法。

头文件代码

// 拾取点获取实体
	static void CmdPtInPoly();
	static void IntersectWithGeRay(const AcDbPolyline *pPoly, const AcGeRay2d &geRay, AcGePoint2dArray& arptIntersect, double tol = 1.0E-7);
	static int FindPoint(const AcGePoint2dArray &points, const AcGePoint2d &point, double tol /*= 1.0E-7*/);
	static bool IsEqual(AcGePoint2d a, AcGePoint2d b, double tol /*=1.0*10E-7*/);
	static	int IsPointInPoly(AcDbPolyline *pPoly, const AcGePoint3d &ptPickWcs, double tol = 1.0E-7);
	// 点是否是多段线的顶点
	static bool PointIsPolyVert(AcDbPolyline *pPoly, const AcGePoint2d &pt, double tol);
	// 从数组中过滤掉重复点
	static void FilterEqualPoints(AcGePoint2dArray &points, double tol = 1.0E-7);
	// 从数组中过滤掉某个点
	static void FilterEqualPoints(AcGePoint2dArray &points, const AcGePoint2d &pt, double tol = 1.0E-7);

源文件代码


void MYLine::IntersectWithGeRay(const AcDbPolyline *pPoly, const AcGeRay2d &geRay, AcGePoint2dArray& arptIntersect, double tol )
{
	arptIntersect.removeAll();

	//设置容差,该容差为两点相同时的容差
	AcGeTol geTol;
	geTol.setEqualPoint(tol);

	// 多段线的每一段分别与射线计算交点
	AcGePoint2d pt2d;
	for (int i = 0; i < static_cast<int>(pPoly->numVerts()); i++)
	{
		if (i < static_cast<int>(pPoly->numVerts() - 1) || pPoly->isClosed() == Adesk::kTrue)
		{
			double dBulge = 0;
			pPoly->getBulgeAt(i, dBulge);
			if (fabs(dBulge) < 1.0E-7)
			{
				// 构建几何类的线段来计算交点
				AcGeLineSeg2d geLine;
				Acad::ErrorStatus es = pPoly->getLineSegAt(i, geLine);
				AcGePoint2d ptIntersect;
				if (geLine.intersectWith(geRay, ptIntersect, geTol) == Adesk::kTrue)
				{
					if (FindPoint(arptIntersect, ptIntersect, tol) < 0)
						arptIntersect.append(ptIntersect);
				}
			}
			else
			{
				// 构建几何类的圆弧来计算交点
				AcGeCircArc2d geArc;
				pPoly->getArcSegAt(i, geArc);
				AcGePoint2d pt1, pt2;
				int iCount = 0;
				if (Adesk::kTrue == geArc.intersectWith(geRay, iCount, pt1, pt2, geTol))
				{
					if (FindPoint(arptIntersect, pt1, tol) < 0)
						arptIntersect.append(pt1);
					if (iCount > 1 && FindPoint(arptIntersect, pt2, tol) < 0)
						arptIntersect.append(pt2);
				}
			}
		}
	}
}
//在数组中查找点,返回点在数组中的索引,如果没有找到点就返回-1
int MYLine::FindPoint(const AcGePoint2dArray &points, const AcGePoint2d &point, double tol /*= 1.0E-7*/)
{
	for (int i = 0; i < points.length(); i++)
	{
		if (IsEqual(points[i], point, tol))
		{
			return i;
		}
	}
	return -1;
}
bool MYLine::IsEqual(AcGePoint2d a, AcGePoint2d b, double tol /*=1.0*10E-7*/)

{
	return (fabs(a.x - b.x) < tol &&
		fabs(a.y - b.y) < tol);
}

int MYLine::IsPointInPoly(AcDbPolyline *pPoly, const AcGePoint3d &ptPickWcs, double tol)
{
	if (!pPoly || !pPoly->isClosed())
		return POINT_POLY_OUTSIDE;

	AcGeTol geTol;
	geTol.setEqualPoint(tol);

	//转换坐标,将点转为ECS坐标系
	AcGePoint3d ptPick;
	acdbWcs2Ecs(asDblArray(ptPickWcs), asDblArray(ptPick), asDblArray(pPoly->normal()), false);

	//判断点和多段线平面是否共面
	double dElevation = pPoly->elevation();
	if (fabs(dElevation - ptPick.z) > tol)
		return POINT_POLY_OUTSIDE;

	//如果点到多段线的最近点和给定的点重合,表示点在多段线上
	AcGePoint3d ptClosestWcs;
	pPoly->getClosestPointTo(ptPickWcs, ptClosestWcs);
	if (ptPickWcs.isEqualTo(ptClosestWcs, geTol))
		return POINT_POLY_ONEDGE;

	//转换最近点为ECS坐标系下
	AcGePoint3d ptClosest;
	acdbWcs2Ecs(asDblArray(ptClosestWcs), asDblArray(ptClosest), asDblArray(pPoly->normal()), false);

	// 第一个射线的方向是从最近点到当前点,起点是当前点
	// 射线的起点是pt,方向为从最近点到pt,如果反向做判断,则最近点距离pt太近的时候,
	// 最近点也会被作为一个交点(这个交点不太容易被排除掉)
	AcGeVector2d vtRay((ptPick - ptClosest).x, (ptPick - ptClosest).y);
	AcGeRay2d geRay(AcGePoint2d(ptPick.x, ptPick.y), vtRay);

	// 判断点和多段线的位置关系
	while (true)
	{
		bool bContinue = false;
		AcGePoint2dArray arptIntersect;
		IntersectWithGeRay(pPoly, geRay, arptIntersect, 1.0E-4);
		FilterEqualPoints(arptIntersect, 1.0E-4);// IntersectWith函数经常会得到很近的交点,这些点必须进行过滤

		if (arptIntersect.length() == 0)
			return POINT_POLY_OUTSIDE;// 没有交点,表示点在多段线的外部
		else
		{
			//特殊情况1:过滤掉由于射线被反向延长带来的影响,当pt距离最近点比较近的时候,
			//最近点竟然被当作一个交点,所以,首先删除最近点(如果有的话)
			FilterEqualPoints(arptIntersect, AcGePoint2d(ptClosest.x, ptClosest.y));

			//特殊情况2:如果某个交点与最近点在给定点的同一方向,要去掉这个点
			//,这个点明显不是交点,还是由于intersectwith函数的Bug
			for (int i = arptIntersect.length() - 1; i >= 0; i--)
			{
				if ((arptIntersect[i].x - ptPick.x) * (ptClosest.x - ptPick.x) >= 0 &&
					(arptIntersect[i].y - ptPick.y) * (ptClosest.y - ptPick.y) >= 0)
					arptIntersect.removeAt(i);
			}

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

					// 将射线旋转一个极小的角度(2度)再次判断(假定这样不会再通过上次判断到的顶点)
					vtRay = vtRay.rotateBy(0.035);
					geRay.set(AcGePoint2d(ptPick.x, ptPick.y), vtRay);
					bContinue = true;
					break;
				}
			}

			if (!bContinue)
			{
				if (0 == arptIntersect.length() % 2)
					return POINT_POLY_OUTSIDE;
				else
					return POINT_POLY_INSIDE;
			}
		}
	}
}
bool MYLine::PointIsPolyVert(AcDbPolyline *pPoly, const AcGePoint2d &pt, double tol)
{
	AcGeTol geTol;
	geTol.setEqualPoint(tol);
	AcGePoint2d ptVert;
	for (int i = 0; i < (int)pPoly->numVerts(); i++)
	{
		pPoly->getPointAt(i, ptVert);
		if (ptVert.isEqualTo(pt, geTol))
		{
			return true;
		}
	}

	return false;
}

// 从数组中过滤掉重复点
void MYLine::FilterEqualPoints(AcGePoint2dArray &points, double tol)
{
	AcGeTol geTol;
	geTol.setEqualPoint(tol);
	for (int i = points.length() - 1; i > 0; i--)
	{
		for (int j = 0; j < i; j++)
		{
			if (points[i].isEqualTo(points[j], geTol))
			{
				points.removeAt(i);
				break;
			}
		}
	}
}
// 从数组中过滤掉某个点
void MYLine::FilterEqualPoints(AcGePoint2dArray &points, const AcGePoint2d &pt, double tol)
{
	AcGeTol geTol;
	geTol.setEqualPoint(tol);
	for (int i = points.length() - 1; i >= 0; i--)
		if (points[i].isEqualTo(pt, geTol))
			points.removeAt(i);
}

测试代码

// 拾取点获取坐标
void MYLine::CmdPtInPoly()
{
	struct resbuf* rb = NULL;
	rb = acutBuildList(RTDXF0, _T("LWPOLYLINE"), RTNONE);
	TCHAR* prompts[2] = { _T("\n请选择了一个实体:"),_T("\n取消了一个实体") };
	ads_name ssPick;
	if (RTNORM == acedSSGet(_T(":S:$-M"), prompts, NULL, rb, ssPick))
	{
		ads_name ent;
		if (RTNORM == acedSSName(ssPick, 0, ent))
		{
			AcDbObjectId id;
			acdbGetObjectId(id, ent);
			AcDbPolyline* pPoly;
			if (Acad::eOk == acdbOpenObject(pPoly, id, AcDb::kForRead))
			{
				ads_point ptRet, wcsPt;
				while (RTNORM == acedGetPoint(nullptr, _T("\n请任意点选一点:"), ptRet))
				{
					// 使用 Adesk::kTrue 替代 RTNORM 判断布尔值
					if (acdbUcs2Wcs(ptRet, wcsPt, Adesk::kFalse) != Adesk::kTrue)
					{
						acutPrintf(_T("\n坐标转换失败!"));
						continue;
					}

					// 2. 创建三维几何点
					AcGePoint3d gePoint3d(wcsPt[0], wcsPt[1], wcsPt[2]);
					// 判断点是否在多段线内
					int iRelation = IsPointInPoly(pPoly, gePoint3d);
					if (POINT_POLY_INSIDE == iRelation)
						acutPrintf(_T("\n\t点在多段线内"));
					else if (POINT_POLY_ONEDGE == iRelation)
						acutPrintf(_T("\n\t点在多段线上"));
					else if (POINT_POLY_OUTSIDE == iRelation)
						acutPrintf(_T("\n\t点在多段线外"));
					else
						acutPrintf(_T("\n\t未知关系"));
				}
				pPoly->close();
			}
		}

		acedSSFree(ssPick);
	}
	acutRelRb(rb);
}

测试代码注册

//初始化我们的命令具体实现,这里写了一个函数统一注册我们的命令
void MYLine::Myinit() 
{
	//注册我们的方法也就是命令,可以理解为acedRegCmds对象addCommand(_T("命令组名"),_T("命令名"),_T("本地提示"),选择常量因为是命令形式调用,调用方法名)添加命令方法
	acedRegCmds->addCommand(_T("My"), _T("Creat2"), _T("判断点是否在实体内"), ACRX_CMD_MODAL, CmdPtInPoly);
}

注册实现

一些需要调用的头文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值