找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1007|回复: 5

[原创] 曲线逆时针判断+点是否在曲线内判断

[复制链接]
发表于 2021-3-3 19:39:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
本帖最后由 Longfin 于 2021-3-5 22:02 编辑

原创:曲线逆时针判断+点是否在曲线内判断----- caoyin


前段时间在修改自用的偏移命令中关于 vla-offset 函数偏移距离正负值判断的问题。
经过好一番琢磨,完美解决问题。由此引出思考,解决了以下两个比较棘手的问题:

1、判断曲线对象是否是逆时针
2、判断点是否在封闭曲线内



一、先从 vlax-curve-getFirstDeriv 说起

我们在判断一个点在直线的方向 (正方向、反方向、在直线上),通常用向量的方法:


已知直线的起点 P1、终点 P2,任意点 P


那么 P1 到 P2 点的向量就是:
(setq V (mapcar '- P2 P1))
此时我们把P1理解为坐标轴的原点,P1到P2表示 X 轴的正方向。


判断点在直线的位置:
(car (trans (mapcar '- P P1) 0 V))
返回值为正表明,点 P 在 Y 轴的正方向;为负则在 Y 轴反方向;为 0 则点 P 在 X 轴上。


明白上面的问题,对于直线的向量我们容易得到,那么曲线如何判断呢?对于圆弧以及样条曲线该如何处理?
vlax-curve-getFirstDeriv 函数帮我们解决了所有问题。我们常常用 vlax-curve-getFirstDeriv 函数来获得曲线上某个点的切线角度,说到这里估计很多人都明白了,对没错。其实最核心的一句话就是:


vlax-curve-getFirstDeriv = 曲线在某个点的切线向量


  1. ;;;--------------------------------------------
  2. ;;;判断点在曲线方向(正方向、反方向、在曲线上)代码如下:
  3. ;;;适用包含直线、圆弧、样条曲线的任意曲线
  4. (defun PT-AtDirOfCurve (E P / P0 V)
  5.   (setq P0 (vlax-curve-getClosestPointTo E P)
  6.         V  (vlax-curve-getFirstDeriv E (vlax-curve-getParamAtPoint E P0))
  7.   )
  8.   (car (trans (mapcar '- P P0) 0 V))
  9. )
  10. ;;测试代码1
  11. (defun C:TT1 (/ E GR1 GR2)
  12.   (if (setq E (car (entsel "\n选择封闭曲线: ")))
  13.     (while
  14.       (progn
  15.         (setq GR1 (grread T 15 0)
  16.               GR2 (cadr GR1)
  17.               GR1 (car GR1)
  18.         )
  19.         (if (or (= GR1 5) (= GR1 3))
  20.           (if (> (PT-AtDirOfCurve E GR2) 0)
  21.             (princ "\r在正方向")
  22.             (princ "\r在反方向")
  23.           )
  24.         )
  25.         (not (member GR1 '(3 11)))
  26.       )
  27.     )
  28.   )
  29.   (princ)
  30. )
  31. ;;;--------------------------------------------



二、封闭曲线对象的逆时针判断

我们上面了解了 vlax-curve-getFirstDeriv 获取切线向量和对于曲线方向的判断的相关问题。


对于闭合(起点、终点重合这里即理解为闭合,而不需要 vlax-curve-isClosed)且不自交的多段线,每个段的两个点都可以得出一个向量,当多段线闭合,所有段的向量就完成一“周”,我们把所有向量相减 ( mapcar '- V1 V2 V3 ....),得到(X Y Z),如果X 、Y 都为正或都为负,则多段线为顺时针,如果一正一负则为顺时针,即(< (* X Y) 0)。


在写出代码之前先说三点问题:
1.对于自交的多段线不适用,当多段线自交一次,则从自交点分开,一边是顺时针,一边是逆时针。
2.因为多段线有可能包含弧段,所以获取每段的向量不能用 vlax-curve-getFirstDeriv 。
3.对于没有闭合的多段线不存在顺时针、逆时针的问题。只存在每个段的方向正反问题。


代码如下:
  1. ;;;--------------------------------------------
  2. ;;; 判断点集是否是逆时针,不支持自交
  3. (defun PTS-CLOCKWISE-P (PTS / X)
  4.   (or (equal (car PTS) (setq X (last PTS)) 1E-8)
  5.       (setq PTS (cons X PTS))
  6.   )
  7.   (defun X (PTS / P1 P2 PTS)
  8.     (if (and (setq P1 (car PTS))
  9.              (setq P2 (cadr PTS))
  10.              (setq PTS (cddr PTS))
  11.         )
  12.       (cons (mapcar '- P2 P1) (X PTS))
  13.     )
  14.   )
  15.   (setq X (apply 'mapcar (cons '- (X PTS))))
  16.   (< (* (car X) (cadr X)) 0)
  17. )

  18. ;;测试代码2
  19. (defun C:TT2 (/ Get-PLINE-VERTEXS E)
  20.   (defun Get-PLINE-VERTEXS (E / X)
  21.     (defun X (N / P)
  22.       (if (setq P (vlax-curve-getPointAtParam E N))
  23.         (cons P (X (1+ N)))
  24.       )
  25.     )
  26.     (X 0)
  27.   )
  28.   (if (setq E (car (entsel "\n选择封闭多段线: ")))
  29.     (Get-PLINE-VERTEXS E)
  30.   )
  31. )
  32. ;;;--------------------------------------------



多段线是否逆时针,容易判断,论坛很容易搜到。那样条曲线怎么办呢?我们必须回归到 vlax-curve-getFirstDeriv 函数。
上面解决了曲线方向的正反问题,给定一个点 我们只能得出该点位于曲线的方向。那么我们能不能先定位一个用于比较点 P ,明确该点位于曲线的方向,将点 P 与它在曲线上的最近点 P0 进行比较。在 P0 点获取切线矢量,如果 P 在曲线的正方向,即可得出该曲线是否是逆时针。


如何给定一个明确方向的点呢?vla-GetBoundingBox,包围盒,可以明确该点位于曲线之外。
代码如下:
  1. ;;;--------------------------------------------
  2. ;;; 判断曲线是否是逆时针,不支持自交曲线
  3. (defun CURVE-CLOCKWISE-P (OBJ / X1 X2 V)
  4.   (vla-GetBoundingBox OBJ 'X1 'X2)
  5.   (setq X1 (mapcar '- (vlax-safearray->list X1) '(1 -1 0))
  6.         X2 (vlax-curve-getClosestPointTo OBJ X1 T)
  7.         V  (vlax-curve-getFirstDeriv OBJ (vlax-curve-getParamAtPoint OBJ X2))
  8.   )
  9.   (> (car (trans (mapcar '- X1 X2) 0 V)) 0)
  10. )
  11. ;;; 测试代码3
  12. (defun C:TT3 (/ E P)
  13.   (if (setq E (entsel "\n选择封闭曲线: "))
  14.     (CURVE-CLOCKWISE-P (vlax-ename->vla-object (car E)))
  15.   )
  16. )
  17. ;;;--------------------------------------------



三、判断点是否在封闭曲线内


其实,搞定任意封闭曲线的逆时针判断,用同样原理就很容易搞定判断点是否在封闭曲线内。
只需要比较曲线外的点和曲线内的点方向是否相同即可,代码如下:

  1. ;;;--------------------------------------------
  2. ;;; 判断点是否在封闭曲线内,暂不支持自交曲线
  3. (defun PT-In-ClosedCurve (O P / X1 X2 X3 V1 V2)
  4.   (vla-GetBoundingBox O 'X1 'X2)
  5.   (setq X1 (mapcar '- (vlax-safearray->list X1) '(1 -1 0))
  6.         X2 (vlax-curve-getClosestPointTo O X1)
  7.         X3 (vlax-curve-getClosestPointTo O P)
  8.         V1 (vlax-curve-getFirstDeriv O (vlax-curve-getParamAtPoint O X2))
  9.         V2 (vlax-curve-getFirstDeriv O (vlax-curve-getParamAtPoint O X3))
  10.   )
  11.   (/= (> (car (trans (mapcar '- X1 X2) 0 V1)) 0)
  12.       (> (car (trans (mapcar '- P X3) 0 V2)) 0)
  13.   )
  14. )
  15. ;;;测试代码4
  16. (defun C:TT4 (/ E P)
  17.   (if (and (setq E (entsel "\n选择封闭曲线: "))
  18.            (setq P (getpoint "\指定点: "))
  19.       )
  20.     (PT-In-ClosedCurve (vlax-ename->vla-object (car E))
  21.                        (trans P 1 0)
  22.     )
  23.   )
  24. )
  25. ;;;--------------------------------------------


根据上面思路,对于自交曲线其实是可以解决的,这个有空再研究吧!








论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!

已领礼包: 27个

财富等级: 恭喜发财

发表于 2021-3-15 10:54:30 | 显示全部楼层
真是学到了,trans函数一直觉得比较难,一直抗拒去学习,原来向量还可以这么用,已经收藏
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 146个

财富等级: 日进斗金

发表于 2021-3-22 08:54:27 | 显示全部楼层
曲線判斷在線的正向反向,實有難度,受教了,感謝分享
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 244个

财富等级: 日进斗金

发表于 2021-9-15 08:22:22 | 显示全部楼层
大佬  威武   厉害!!!!!!!
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 3个

财富等级: 恭喜发财

发表于 2022-7-3 21:52:31 | 显示全部楼层
算法还是不够完善,经常判断错误,应该是逻辑不正确。发了一个示例图形,你可以测试一下!

判断出错的例子.zip

15.87 KB, 下载次数: 7, 下载积分: D豆 -1 , 活跃度 1

论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 244个

财富等级: 日进斗金

发表于 2022-7-4 11:12:20 | 显示全部楼层
十分感谢大佬的辛苦钻研   还能分享给我们
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|辽公网安备|晓东CAD家园 ( 辽ICP备15016793号 )

GMT+8, 2024-11-22 09:45 , Processed in 0.215529 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表