马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
本帖最后由 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 = 曲线在某个点的切线向量
- ;;;--------------------------------------------
- ;;;判断点在曲线方向(正方向、反方向、在曲线上)代码如下:
- ;;;适用包含直线、圆弧、样条曲线的任意曲线
- (defun PT-AtDirOfCurve (E P / P0 V)
- (setq P0 (vlax-curve-getClosestPointTo E P)
- V (vlax-curve-getFirstDeriv E (vlax-curve-getParamAtPoint E P0))
- )
- (car (trans (mapcar '- P P0) 0 V))
- )
- ;;测试代码1
- (defun C:TT1 (/ E GR1 GR2)
- (if (setq E (car (entsel "\n选择封闭曲线: ")))
- (while
- (progn
- (setq GR1 (grread T 15 0)
- GR2 (cadr GR1)
- GR1 (car GR1)
- )
- (if (or (= GR1 5) (= GR1 3))
- (if (> (PT-AtDirOfCurve E GR2) 0)
- (princ "\r在正方向")
- (princ "\r在反方向")
- )
- )
- (not (member GR1 '(3 11)))
- )
- )
- )
- (princ)
- )
- ;;;--------------------------------------------
二、封闭曲线对象的逆时针判断
我们上面了解了 vlax-curve-getFirstDeriv 获取切线向量和对于曲线方向的判断的相关问题。
对于闭合(起点、终点重合这里即理解为闭合,而不需要 vlax-curve-isClosed)且不自交的多段线,每个段的两个点都可以得出一个向量,当多段线闭合,所有段的向量就完成一“周”,我们把所有向量相减 ( mapcar '- V1 V2 V3 ....),得到(X Y Z),如果X 、Y 都为正或都为负,则多段线为顺时针,如果一正一负则为顺时针,即(< (* X Y) 0)。
在写出代码之前先说三点问题:
1.对于自交的多段线不适用,当多段线自交一次,则从自交点分开,一边是顺时针,一边是逆时针。
2.因为多段线有可能包含弧段,所以获取每段的向量不能用 vlax-curve-getFirstDeriv 。
3.对于没有闭合的多段线不存在顺时针、逆时针的问题。只存在每个段的方向正反问题。
代码如下:
- ;;;--------------------------------------------
- ;;; 判断点集是否是逆时针,不支持自交
- (defun PTS-CLOCKWISE-P (PTS / X)
- (or (equal (car PTS) (setq X (last PTS)) 1E-8)
- (setq PTS (cons X PTS))
- )
- (defun X (PTS / P1 P2 PTS)
- (if (and (setq P1 (car PTS))
- (setq P2 (cadr PTS))
- (setq PTS (cddr PTS))
- )
- (cons (mapcar '- P2 P1) (X PTS))
- )
- )
- (setq X (apply 'mapcar (cons '- (X PTS))))
- (< (* (car X) (cadr X)) 0)
- )
- ;;测试代码2
- (defun C:TT2 (/ Get-PLINE-VERTEXS E)
- (defun Get-PLINE-VERTEXS (E / X)
- (defun X (N / P)
- (if (setq P (vlax-curve-getPointAtParam E N))
- (cons P (X (1+ N)))
- )
- )
- (X 0)
- )
- (if (setq E (car (entsel "\n选择封闭多段线: ")))
- (Get-PLINE-VERTEXS E)
- )
- )
- ;;;--------------------------------------------
多段线是否逆时针,容易判断,论坛很容易搜到。那样条曲线怎么办呢?我们必须回归到 vlax-curve-getFirstDeriv 函数。
上面解决了曲线方向的正反问题,给定一个点 我们只能得出该点位于曲线的方向。那么我们能不能先定位一个用于比较点 P ,明确该点位于曲线的方向,将点 P 与它在曲线上的最近点 P0 进行比较。在 P0 点获取切线矢量,如果 P 在曲线的正方向,即可得出该曲线是否是逆时针。
如何给定一个明确方向的点呢?vla-GetBoundingBox,包围盒,可以明确该点位于曲线之外。
代码如下:
- ;;;--------------------------------------------
- ;;; 判断曲线是否是逆时针,不支持自交曲线
- (defun CURVE-CLOCKWISE-P (OBJ / X1 X2 V)
- (vla-GetBoundingBox OBJ 'X1 'X2)
- (setq X1 (mapcar '- (vlax-safearray->list X1) '(1 -1 0))
- X2 (vlax-curve-getClosestPointTo OBJ X1 T)
- V (vlax-curve-getFirstDeriv OBJ (vlax-curve-getParamAtPoint OBJ X2))
- )
- (> (car (trans (mapcar '- X1 X2) 0 V)) 0)
- )
- ;;; 测试代码3
- (defun C:TT3 (/ E P)
- (if (setq E (entsel "\n选择封闭曲线: "))
- (CURVE-CLOCKWISE-P (vlax-ename->vla-object (car E)))
- )
- )
- ;;;--------------------------------------------
三、判断点是否在封闭曲线内
其实,搞定任意封闭曲线的逆时针判断,用同样原理就很容易搞定判断点是否在封闭曲线内。
只需要比较曲线外的点和曲线内的点方向是否相同即可,代码如下:
- ;;;--------------------------------------------
- ;;; 判断点是否在封闭曲线内,暂不支持自交曲线
- (defun PT-In-ClosedCurve (O P / X1 X2 X3 V1 V2)
- (vla-GetBoundingBox O 'X1 'X2)
- (setq X1 (mapcar '- (vlax-safearray->list X1) '(1 -1 0))
- X2 (vlax-curve-getClosestPointTo O X1)
- X3 (vlax-curve-getClosestPointTo O P)
- V1 (vlax-curve-getFirstDeriv O (vlax-curve-getParamAtPoint O X2))
- V2 (vlax-curve-getFirstDeriv O (vlax-curve-getParamAtPoint O X3))
- )
- (/= (> (car (trans (mapcar '- X1 X2) 0 V1)) 0)
- (> (car (trans (mapcar '- P X3) 0 V2)) 0)
- )
- )
- ;;;测试代码4
- (defun C:TT4 (/ E P)
- (if (and (setq E (entsel "\n选择封闭曲线: "))
- (setq P (getpoint "\指定点: "))
- )
- (PT-In-ClosedCurve (vlax-ename->vla-object (car E))
- (trans P 1 0)
- )
- )
- )
- ;;;--------------------------------------------
根据上面思路,对于自交曲线其实是可以解决的,这个有空再研究吧!
|