企业网站建设实训小结,wordpress文章没办法显示略缩图,wordpress没有上级目录的写权限,设计师之家官网首页目录 1 基本思路2 相关知识点2.1 ECS坐标系概述2.2 其他点坐标转换接口2.3 如何获取多段线的顶点ECS坐标 3 实现例程3.1 接口实现3.2 测试代码 4 实现效果 在CAD的二次开发中#xff0c;点和多段线的关系是一个非常重要且常见的问题#xff0c;本文实现例程以张帆所著《Objec… 目录 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 points.length(); i){pt1 points[i];if (IsEqual(pt1, pt2, tol)){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 pPoly-numVerts(); i){if (i 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);}}}}
}// 点是否是多段线的顶点
static bool 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;
}// 从数组中过滤掉重复点
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 i; j){if (points[i].isEqualTo(points[j],geTol)){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 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;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 实现效果