ObjectARX如何判断点和多段线的关系

您所在的位置:网站首页 如何将线变为多段线 ObjectARX如何判断点和多段线的关系

ObjectARX如何判断点和多段线的关系

2023-06-12 19:51| 来源: 网络整理| 查看: 265

目录 1 基本思路2 相关知识点2.1 ECS坐标系概述2.2 其他点坐标转换接口2.3 如何获取多段线的顶点ECS坐标 3 实现例程3.1 接口实现3.2 测试代码 4 实现效果

 在CAD的二次开发中,点和多段线的关系是一个非常重要且常见的问题,本文实现例程以张帆所著《ObjectARX 开发基础与实例教程》为基础,并完善和修复了部分问题。

1 基本思路

 点和多段线的关系判断算法有两个思路:叉乘判断法(适用于凸多边形)和射线法,本文以射线法进行代码实现。其基本思路为:

如果点在多段线上,返回该结果。从给定点出发,沿某个方向做一条射线,计算射线与多边形交点的数量。如果交点数量为奇数,那么点在图形内部;如果交点数量为偶数,那么点在图形外部。在第2条的基础上,排除交点在多段线的顶点上的情况;若出现该情况,需要旋转射线重新判断。 2 相关知识点 2.1 ECS坐标系概述

 在该例程中,有一个关键点是理解ECS(或者OCS)坐标系统。  ECS是实体(对象)坐标系,其原点是WCS的原点,X和Y轴所在平面的法向量是实体的法向量。ECS的 X 轴和Y轴的方向由任意轴算法确定,也就是X、Y轴的方向是由法向量与 WCS 的关系来确定的。因此,ECS的X 轴和Y轴是唯一且仅由法向量决定的。另外,ECS的Z坐标是指xy平面距离WCS原点的距离(有正负)。  在ObjectARX有一个暴露的接口可以完成WCS→ECS的转换

bool acdbWcs2Ecs(ads_point p,ads_point q,ads_point norm,bool vec);

 上述接口的实现应该是下列办法

//将WCS转为一个平面实体的ECS坐标 int Wcs2Ecs(const AcGeVector3d vtNorm, const AcGePoint3d ptWcs, BOOL bDisp, AcGePoint3d& ptEcs) {struct resbuf fromrb, torb;fromrb.restype = RTSHORT;fromrb.resval.rint = 0; // WCS torb.restype = RT3DPOINT;ads_point_set(asDblArray(vtNorm), torb.resval.rpoint);return acedTrans(asDblArray(ptWcs), &fromrb, &torb, bDisp, asDblArray(ptEcs)); }

 理解了上述知识点,我们之后在判断射线和多段线关系的时候,就可以把射线转为多段线的ECS坐标,然后在多段线所在的ECS平面上,比较射线和多段线的每一条线段的相交关系。从而可以优化算法。

2.2 其他点坐标转换接口 点或向量坐标变换 函数名作用acdbUcs2EcsUCS→ECSacdbEcs2UcsECS→UCSacdbUcs2WcsUCS→WCSacdbWcs2UcsWCS→UCSacdbEcs2WcsECS→WCSacdbWcs2EcsWCS→ECS AcGePoint3d和ads_point互转 函数名作用AcGePoint3d (AcGePoint2d ) → ads_pointasDblArrayads_point → AcGePoint3d (AcGePoint2d )aspnt3d 或 asPnt2d 2.3 如何获取多段线的顶点ECS坐标

 多段线获取顶点坐标有两种重载形式:

Acad::ErrorStatus getPointAt(unsigned int, AcGePoint3d& pt ) const; Acad::ErrorStatus getPointAt(unsigned int index, AcGePoint2d& pt ) const;

 其中第一种获取的是顶点的WCS坐标,第二种获取的是顶点的ECS 2D坐标。这个区别一定要看仔细。

3 实现例程 3.1 接口实现

 该例程在张帆所著方法的基础上,增加了对UCS坐标系的支持。在此,再次向原书作者表达敬意。

#include "dbxutil.h" #define POINT_POLY_INSIDE 1 #define POINT_POLY_OUTSIDE 0 #define POINT_POLY_ONEDGE 2// 在数组中查找某个点,返回点在数组中的索引,未找到则返回-1 int FindPoint(const AcGePoint2dArray &points, const AcGePoint2d &point, double tol /*= 1.0E-7*/) {TADSGePoint3d pt1;TADSGePoint3d pt2(point);for (int i = 0; i return i;}}return -1; }//-----------------------------------------------------------------------------+ //=Description: 几何类射线和多段线的交点 //=Parameter: pPoly[in] 多段线 //=Parameter: geRay[in] 射线(位于多段线的OCS平面上) //=Parameter: arptIntersect[in] 返回的交点(坐标系为多段线的OCS坐标系) //=Parameter: tol[in] 容差 //-----------------------------------------------------------------------------+ static void IntersectWithGeRay(const AcDbPolyline *pPoly, const AcGeRay2d &geRay, AcGePoint2dArray& arptIntersect, double tol = 1.0E-7) {arptIntersect.removeAll();//设置容差,该容差为两点相同时的容差AcGeTol geTol;geTol.setEqualPoint(tol);// 多段线的每一段分别与射线计算交点AcGePoint2d pt2d;for (int i = 0; i numVerts(); i++){if (i numVerts() - 1 || pPoly->isClosed() == Adesk::kTrue){double dBulge = 0;pPoly->getBulgeAt(i, dBulge);if (fabs(dBulge) if (FindPoint(arptIntersect, ptIntersect, tol) if (FindPoint(arptIntersect, pt1, tol) 1 && FindPoint(arptIntersect, pt2, tol) pPoly->getPointAt(i, ptVert);if (ptVert.isEqualTo(pt, geTol)){return true;}}return false; }// 从数组中过滤掉重复点 static void FilterEqualPoints(AcGePoint2dArray &points, double tol = 1.0E-7) {AcGeTol geTol;geTol.setEqualPoint(tol);for (int i = points.length() - 1; i > 0; i--){for (int j = 0; j points.removeAt(i);break;}}} }// 从数组中过滤掉某个点 static void FilterEqualPoints(AcGePoint2dArray &points, const AcGePoint2d &pt, double tol = 1.0E-7) {AcGeTol geTol;geTol.setEqualPoint(tol);for (int i = points.length() - 1; i >= 0; i--)if (points[i].isEqualTo(pt, geTol))points.removeAt(i); }//-----------------------------------------------------------------------------+ //=Description: 判断点是否在多段线内部 //=Return: 0多段线外,1多段线内,2多段线上 //=Parameter: pPoly[in] //=Parameter: ptPickWcs[in] //=Parameter: tol[in] //-----------------------------------------------------------------------------+ int IsPointInPoly(AcDbPolyline *pPoly, const AcGePoint3d &ptPickWcs, double tol = 1.0E-7) {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函数的Bugfor (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 (i = 0; i // 处理给定点很靠近多段线顶点的情况(如果与顶点距离很近,就认为这个点在多段线上,因为这种情况没有什么好的判断方法)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;elsereturn POINT_POLY_INSIDE;}}} } 3.2 测试代码 void 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;while (RTNORM == acedGetPoint(NULL, _T("\n请任意点选一点:"), ptRet)){//判断点是否在多段线以内int iRelation = IsPointInPoly(pPoly, Ucs2Wcs(ptRet));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点在多段线外"));}pPoly->close();}}acedSSFree(ssPick);}acutRelRb(rb); } 4 实现效果

在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3