目录

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

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

1 基本思路

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

  1. 如果点在多段线上,返回该结果。
  2. 从给定点出发,沿某个方向做一条射线,计算射线与多边形交点的数量。如果交点数量为奇数,那么点在图形内部;如果交点数量为偶数,那么点在图形外部。
  3. 在第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 其他点坐标转换接口

  1. 点或向量坐标变换
函数名 作用
acdbUcs2Ecs UCS→ECS
acdbEcs2Ucs ECS→UCS
acdbUcs2Wcs UCS→WCS
acdbWcs2Ucs WCS→UCS
acdbEcs2Wcs ECS→WCS
acdbWcs2Ecs WCS→ECS
  1. AcGePoint3d和ads_point互转
函数名 作用
AcGePoint3d (AcGePoint2d ) → ads_point asDblArray
ads_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 实现效果

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

  1. AUTOCAD中判断点在多段线内部的办法

    AUTOCAD中判断点在多段线内部的办法 摘录自张帆的<AutoCAD ObjectARX(VC)开发基础与实例教程> 判断给定点在多段线内: 1.获得多段线的顶点信息 和 给定点信息 2 ...

  2. c语言 两条线段位置,C++/STL实现判断平面内两条线段的位置关系代码示例

    概念 平面内两条线段位置关系的判定在很多领域都有着广泛的应用,比如游戏.CAD.图形处理等,而两线段交点的求解又是该算法中重要的一环.本文将尽可能用通俗的语言详细的描述一种主流且性能较高的判定算法. ...

  3. 位置关系C语言,C++/STL实现判断平面内两条线段的位置关系代码示例

    概念 平面内两条线段位置关系的判定在很多领域都有着广泛的应用,比如游戏.CAD.图形处理等,而两线段交点的求解又是该算法中重要的一环.本文将尽可能用通俗的语言详细的描述一种主流且性能较高的判定算法. ...

  4. 关于objectArx /CAD二次开发中“绘制点、线、圆、圆弧、多段线、矩形、文字等”

    使用CAD软件绘图时,所有的图元均可由点.线.圆.圆弧.多段线.文字等构成,使用objectArx绘制图元时可调用相应的接口函数,如AcDbLine绘制直线. 1.绘制点 参数:pt(点的位置) Ac ...

  5. DXF文件中将多段线凸度转换成圆弧

    在数控系统中,我们常常会将DXF文件转换成G代码,主要涉及到 直线.多段线.圆.圆弧.椭圆.样条曲线.点,七大类,在转换成G代码过程中,其中样条曲线和多段线中凸度问题是比较复杂一些,而样条曲线的转换网 ...

  6. 图形/多段线内缩外扩思路

    图形/多段线内缩外扩思路 前言 我在网上找了很多关于多边形内缩外扩的资料,也测试了一些算法库,如Clipper,boost::geometry::buffer,其实都可以得到不错的结果. Clippe ...

  7. 11.轻量多段线(LwPolyline)

    愿你出走半生,归来仍是少年! 环境:.NET FrameWork4.5.ObjectArx 2016 64bit 目录 1.新建 2.质心 3.延长 LwPolyline是CAD中最常用的实体类型,在 ...

  8. c# cad二次开发 通过选择txt文件将自动转换成多段线

    c# cad二次开发 通过选择txt文件将自动转换成多段线,txt样式如下 using System; using System.Collections.Generic; using System.T ...

  9. c# CAD二次开发 类库 创建各种图形、直线、圆、多段线、正方形、点等

    c# CAD二次开发 类库 创建各种图形.直线.圆.多段线.正方形.点等 using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD ...

最新文章

  1. cuDNN version incompatibility: PyTorch was compiled against 7005 but linked against 6021 问题解决...
  2. java复习2(编码)
  3. Android之SubsamplingScaleImageView加载长图不能放缩问题
  4. 【剑指offer】面试题32:从上到下打印二叉树(java)
  5. JAVA入门级教学之(char类型)
  6. 资源放送丨《 Kubernetes Operator 开发范式》PPT视频
  7. 项目 协程-实现非抢占式TCP服务器
  8. 生产者消费者代码c语言_由生产者消费者模型引出的线程同步问题
  9. 城市统计年鉴数据查询
  10. 在mysql中 使用in关键字实现子查询_MySQL 之子查询
  11. c++ 运算符重载(简单易懂)
  12. 利用IPSec/L2TP代理上网
  13. 带有图形界面的通用趋势
  14. 【数学】 海 盗 埋 宝
  15. 纠正你读错的电脑英文词汇(程序员真的可以看看)
  16. 微信公众号之错误返回码
  17. SQL解决Error converting data type nvarchar to numeric.
  18. 美的美少年暑期实习面经
  19. php懒惰模式,PHP正则贪婪/懒惰匹配模式
  20. UltraISO制作系统ISO镜像

热门文章

  1. Python爬虫:想听榜单歌曲?使用BeautifulSoup库只需要14行代码即可搞定
  2. php 读写网络共享文件,php读取局域网共享文件
  3. android 三星手机拍照报错
  4. 高通提交对苹果公司诉讼答辩 同时提起反诉
  5. 小程序:通过小程序wx.getLocation接口审核的经验总结
  6. 真正有效的U盘分区方法
  7. 放射科信息服务器瘫痪,放射科网络瘫痪应急预案南区
  8. 怒:阿里妈妈 在我Blog投的破广告
  9. linux qt如何获取权限,如何让Qt程序在运行时获取UAC权限
  10. 汤晓鸥徐立等自愿锁定商汤股票2年/ 索尼新动捕全套48g/ 百度欲扩大RoboTaxi运营范围…今日更多新鲜事在此...