|
最小包围盒和最大距离点对在实际生活中有很多运用。
譬如,最小包围盒指的是能包围一个多边形或者一些多边形的最小面积(可能周长)的这样的一个矩形,这个帖子我只是讨论二维的,所以说是矩形,
求得最小包围和就能合理利用材料,选择合适的截面。
在这个算法中,采用了“游标卡尺”的算法,所以运行速度很快,在求出一个形状的凸包之后,就能迅速得到最小矩形。O(n *log(n))的时间复杂度,
大量运算成为可能。
对于CAD本身来说,用getboundingBox得出的结果仅仅是平行与WCS的坐标系统的矩形,而且有时候很不准确,特别对于spline来说,有时候相差很大。
然而,WCS下的boundingBox一般来说不是最小矩形,如果要得到最小的,必须要旋转这个图形很多次才能得到结果,但这时一个费事费力的事情。
所以,我们有必要考虑一种算法。
这些天通过几个比较晚的晚上,终于得出了算法。为了增加讨论效果,我在这里先贴出arx文件,适用于2004-2006平台。至于LISP,以后在贴出。
速度之快,几乎是0秒。
用法: 先输入命令 Hull,
然后输入分弧精度---指的是对样条曲线或者弧形的分段数目,取值在100-2000比较合理,太大容易引起问题且也不能有效提高精度。
选择图形中的多边形或者样条曲线,然后你就可以看到结果了。最远距离用红线标出。
- ;;;The procedure for Test
- (defun C:test (/ PP PTLIST SEL T0 n)
- (princ "\n命令是test(The command is Test).")
- (princ "\nPlease select LWPOLYLINE,LINE,SPLINE,POINT")
- (setq sel (ssget (list '(0 . "POINT,LWPOLYLINE,LINE,SPLINE")))) ;select curve or point
- (initget 7)
- (setq n (getint "\nPlease Enter the precision of dividing curve(The resonable value is 100 to 2000):"))
- (if sel
- (progn
- (setq ptlist (getpt sel n)) ;construct the set of points
- (setq ptlist (Graham-scan ptlist)) ;construct the CCW Hull of this set.
- (if (<= (det (car ptlist) (cadr ptlist) (caddr ptlist)) 0.0) ;ensure the hull is CCW.
- (setq ptlist (reverse ptlist)) ;if it isn't CCW,then reverse it
- )
- (setq t0 (getvar "TDUSRTIMER")) ;The start time of this algorithm
- (setq pp (car (MinAreaRectangle ptlist))) ;start calculating
- (princ "\nIt takes :")
- (princ (* (- (getvar "TDUSRTIMER") t0) 86400)) ;The End time
- (princ "seconds")
- (if pp
- (make-poly pp) ;draw rectangle.
- )
- )
- )
- (princ)
- )
- ;;;=======================================================
- ;;;Function : Find the minimum area of encasing rectangle.
- ;;;Arguments : A CCW HULL
- ;;;Return: The Four points of Rectangle and its Area
- ;;;=======================================================
- (defun MinAreaRectangle (ptlist / AA AI BB D1
- D2 EDGE I I1X I1Y I2X I2Y
- IL INF IX IY J1 J2 MINA
- MINH MINW NORH NORM PI1 PI2 PTI0
- PTI1 PTI2 PTJ1 PTK1 PTM1 PTS1 PTS2
- PTS3 PTS4 REC1 REC2 REC3 REC4 RECT
- VECH VECL VJ12 VM12
- )
- (setq INF 1e309)
- (setq minA INF) ;Initiating the Minimum area is infinite
- (setq pti0 (car ptlist)) ;the first point of Hull.
- (setq pts1 (append ptlist (list pti0))) ;add the first point at back of Hull
- (setq pts2 (cdr (append ptlist ptlist (list pti0)))) ;Construct a loop for the hull
- (setq i 0)
- ;;Find area of encasing rectangle anchored on each edge.
- (repeat (length ptlist)
- (setq pi1 (car pts1)
- pi2 (cadr pts1)
- i1x (car pi1)
- i1y (cadr pi1)
- i2x (car pi2)
- i2y (cadr pi2)
- ix (- i2x i1x)
- iy (- i2y i1y)
- il (distance (list ix iy) '(0.0 0.0))
- )
- ;;寻找最左点
- ;;Find a vertex on on first perpendicular line of support
- (while (> (DOTPR ix iy pts2) 0.0)
- (setq pts2 (cdr pts2))
- )
- ;;寻找最上点
- ;;Find a vertex on second perpendicular line of suppoer
- (if (= i 0)
- (setq pts3 pts2)
- )
- (while (> (CROSSPR ix iy pts3) 0.0)
- (setq pts3 (cdr pts3))
- )
- ;;寻找最右点
- ;;Find a vertex on second perpendicular line of suppoer
- (if (= i 0)
- (setq pts4 pts3)
- )
- (while (< (DOTPR ix iy pts4) 0.0)
- (setq pts4 (cdr pts4))
- )
- ;;得出了每边的矩形
- ;;Find distances between parallel and perpendicular lines of support
- (cond
- ((equal i1x i2x 1e-4) ;如果边两点的X值相同
- (setq d1 (- (caar pts3) i1x) ;那么矩形的高就是最上点与边的X的差值
- d2 (- (cadar pts4) (cadar pts2)) ;矩形的宽就是最左和最右的Y的差值
- )
- )
- ((equal i1y i2y 1e-4) ;如果边两点的Y值相同
- (setq d1 (- (cadar pts3) i1y) ;那么矩形的高就是最上点与边的Y的差值
- d2 (- (caar pts4) (caar pts2)) ;矩形的宽就是最左和最右的X的差值
- )
- )
- (T
- (setq aa (det pi1 pi2 (car pts3))) ;否则计算边和最上点构成的面积的二倍(det)
- (setq d1 (/ aa il)) ;高就是det值除以边长
- (setq j1 (car pts2)) ;最右边点
- (setq j2 (list (- (car j1) iy) (+ (cadr j1) ix))) ;通过最右边点的垂直边的点
- (setq bb (det j1 j2 (car pts4))) ;最右边点,上面的点和最左边的点
- (setq d2 (/ bb il)) ;这三点的det除以边长就是宽
- )
- )
- ;;计算矩形的面积,必要时更新最小面积
- ;;Compute area of encasing rectangle anchored on current edge.
- ;;if the area is smaller than the old Minimum area,then update,and record the width,height and five points.
- (setq Ai (abs (* d1 d2))) ;面积就是高和宽的积
- (if (< Ai MinA) ;如果面积小于先前的最小面积,则记录:
- (setq MinA Ai ;更新最小面积
- MinH d1 ;最小面积的高
- MinW d2 ;最小面积的宽
- pti1 pi1 ;最小面积的边的第一个端点
- pti2 pi2 ;最小面积的边的第二个端点
- ptj1 (car pts2) ;最右边的点
- ptk1 (car pts3) ;最上面的点
- ptm1 (car pts4) ;最左边的点
- )
- )
- (setq pts1 (cdr pts1)) ;检测下一条边
- (setq i (1+ i)) ;计数器加一
- )
- ;;according to the result ,draw the Minimum Area Rectangle
- (setq edge (mapcar '- pti2 pti1)) ;最小面积的边对应的向量
- (setq VecL (distance edge '(0.0 0.0))) ;最小面积的边的长度
- (setq NorH (abs (/ MinH VecL))) ;这边的法线
-
- (setq Norm (list (- (cadr edge)) (car edge))) ;这边的垂直向量
- (setq vj12 (mapcar '+ ptj1 Norm)) ;通过最右点的垂直向量
- (setq vm12 (mapcar '+ ptm1 Norm)) ;通过最左点的垂直向量
- (setq vecH (mapcar '* (list NorH NorH) Norm))
- (setq rec1 (inters pti1 pti2 ptj1 vj12 nil)) ;矩形的第一点
- (setq rec4 (inters pti1 pti2 ptm1 vm12 nil)) ;矩形的第四点
- (setq rec2 (mapcar '+ rec1 vecH)) ;矩形的第二点
- (setq rec3 (mapcar '+ rec4 vecH)) ;矩形的第三点
- (setq rect (list Rec1 rec2 rec3 rec4)) ;矩形的点表
- (cons rect MinA) ;返回这个矩形的点表和最大距离
- )
- ;;;========================================
- ;;;求凸壳的直径的程序
- ;;;参数:逆时针的凸壳 H-------注意逆时针!!!
- ;;;返回值: 直径的两个端点和直径 Pair . MaxD
- ;;;========================================
- (defun Max-distance (H / D M MAXD P PAIR Q U V W)
- (setq Q (cdr (append H H (list (car H))))) ;构造一个首尾循环的凸集,且起始点为凸壳的第二点
- (setq MaxD 0.0) ;初始化最小距离为0
- (foreach U H ;依次检查凸壳的边
- (setq V (car Q)) ;循环集的第一点
- (setq W (cadr Q)) ;循环集的第二点
- (setq M (mid-pt V W)) ;这两点的中点
- (while (> (dot M U V) 0.0) ;如果夹角小于90度(即点积大于0)
- (setq Q (cdr Q)) ;循环集推进
- (setq V (car Q)) ;取下一点
- (setq W (cadr Q)) ;下下一点
- (setq M (mid-pt V W)) ;这两点的中点
- )
- (setq D (distance U V)) ;计算这时的最大距离
- (if (> D MaxD) ;如果大于前面的最大距离
- (setq MaxD D ;就替换前面的最大距离
- Pair (list U V) ;并记录这对点
- )
- )
- )
- (cons Pair MaxD) ;返回这对点和最大距离
- )
- ;;;==================
- ;;;Graham扫描法求凸包
- ;;;==================
- (defun Graham-scan (ptlist / hullpt maxXpt sortPt P Q)
- (if (< (length ptlist) 3) ;3点以下
- ptlist ;是本集合
- (progn
- (setq maxXpt (assoc (apply 'max (mapcar 'car ptlist)) ptlist)) ;最右边的点
- (setq sortPt (sort-by-angle-distance ptlist maxXpt)) ;分类点集
- (setq hullPt (list (cadr sortPt) maxXpt)) ;开始的两点
- (foreach n (cddr sortPt) ;从第3点开始
- (setq hullPt (cons n HullPt)) ;把Pi加入到凸集
- (setq P (cadr hullPt)) ;Pi-1
- (setq Q (caddr hullPt)) ;Pi-2
- (while (and q (> (det n P Q) -1e-6)) ;如果左转
- (setq hullPt (cons n (cddr hullPt))) ;删除Pi-1点
- (setq P (cadr hullPt)) ;得到新的Pi-1点
- (setq Q (caddr hullPt)) ;得到新的Pi-2点
- )
- )
- (reverse hullpt) ;返回凸集
- )
- )
- )
- ;;;中点函数
- (defun mid-pt (p1 p2)
- (list
- (* (+ (car p1) (car p2)) 0.5)
- (* (+ (cadr p1) (cadr p2)) 0.5)
- )
- )
- ;;;以某点为基点,按照角度和距离分类点集
- (defun sort-by-angle-distance (ptlist pt / )
- (vl-sort ptlist
- (function
- (lambda (e1 e2 / ang1 ang2 )
- (setq ang1 (angle pt e1))
- (setq ang2 (angle pt e2))
- (if (= ang1 ang2)
- (< (distance pt e1) (distance pt e2))
- (< ang1 ang2)
- )
- )
- )
- )
- )
- ;;;点积= x1*x2 + y1*y2
- (defun DOTPR (ix iy pts / pt1 pt2)
- (setq pt1 (car pts))
- (setq pt2 (cadr pts))
- (+ (* ix (- (car pt2) (car pt1)))
- (* iy (- (cadr pt2) (cadr pt1)))
- )
- )
- ;;;叉积= x1*y2 - x2*y1
- (defun CROSSPR (ix iy pts / pt1 pt2)
- (setq pt1 (car pts))
- (setq pt2 (cadr pts))
- (- (* ix (- (cadr pt2) (cadr pt1)))
- (* iy (- (car pt2) (car pt1)))
- )
- )
- ;;;中点函数
- (defun mid-pt (p1 p2)
- (list
- (* (+ (car p1) (car p2)) 0.5)
- (* (+ (cadr p1) (cadr p2)) 0.5)
- )
- )
- ;;;定义三点的行列式,即三点之倍面积
- (defun det (p1 p2 p3 / x2 y2)
- (setq x2 (car p2)
- y2 (cadr p2)
- )
- (- (* (- x2 (car p3)) (- y2 (cadr p1)))
- (* (- x2 (car p1)) (- y2 (cadr p3)))
- )
- )
- ;;;定义向量的点积函数
- (defun dot (p1 p2 p3 / x1 y1)
- (setq x1 (car p1)
- y1 (cadr p1)
- )
- (+ (* (- (car p2) x1) (- (car p3) x1))
- (* (- (cadr p2) y1) (- (cadr p3) y1))
- )
- )
- ;;;取点函数2
- (defun getpt (ss n / i s a b c d e)
- (setq i 0)
- (if ss
- (repeat (sslength ss)
- (setq a (ssname ss i))
- (setq b (entget a))
- (setq e (cdr (assoc 0 b)))
- (cond
- ((= e "LWPOLYLINE")
- (setq c (get-pline-vertexs a n))
- (setq s (append c s))
- )
- ((= e "SPLINE")
- (setq c (get-spline-vertexs a n))
- (setq s (append c s))
- )
- ((= e "LINE")
- (setq c (cdr (assoc 10 b)))
- (setq d (cdr (assoc 11 b)))
- (setq c (list (car c) (cadr c)))
- (setq d (list (car d) (cadr d)))
- (setq s (cons c s))
- (setq s (cons d s))
- )
- ((= e "POINT")
- (setq c (cdr (assoc 10 b)))
- (setq c (list (car c) (cadr c)))
- (setq s (cons c s))
- )
- )
- (setq i (1+ i))
- )
- )
- s
- )
- ;;取得多边形顶点
- (defun get-LWpolyline-vertexs (DXF / lst)
- (foreach n DXF
- (if (= (car n) 10)
- (setq lst (cons (cdr n) lst))
- )
- )
- (reverse lst)
- )
- (defun get-3dpolyline-vertexs ( ent / p )
- (if (and (setq ent (entnext ent)) (setq p (cdr (assoc 10 (entget ent)))))
- (cons p (get-3dpolyline-vertexs ent))
- )
- p
- )
- ;;;取得样条曲线的点
- (defun get-spline-vertexs (ent n / DIST ENDPAR I LEN OBJ PT PTS SEG)
- (setq obj (vlax-ename->vla-object ent))
- (setq endpar (vlax-curve-getEndParam obj))
- (setq len (vlax-curve-getDistAtParam obj endpar))
- (setq seg (/ len n))
- (setq dist 0)
- (while (< dist len)
- (setq pt (vlax-curve-getPointAtDist obj dist))
- (setq pts (cons pt pts))
- (setq dist (+ seg dist))
- )
- (if (= (vla-get-closed obj) :vlax-false)
- (setq pt (vlax-curve-getEndPoint obj)
- pts (cons pt pts)
- )
- )
- (reverse pts)
- )
- ;;;取得含有圆弧的多段线的点
- (defun get-pline-vertexs (ent n / BLG DIST ENDPAR I L1 L2 L3 LI OBJ PT PTS VEXNUM)
- (setq obj (vlax-ename->vla-object ent))
- (setq endpar (vlax-curve-getEndParam obj))
- (setq vexNum (fix endPar))
- (setq pts nil)
- (setq i 0)
- (repeat vexNum
- (setq pt (vlax-curve-getPointAtParam obj i))
- (setq pts (cons pt pts))
- (setq blg (vla-getbulge obj i))
- (if (/= blg 0.0)
- (progn
- (setq l1 (vlax-curve-getDistAtParam obj i))
- (setq l2 (vlax-curve-getDistAtParam obj (1+ i)))
- (setq l3 (- l2 l1))
- (setq li (/ l3 n))
- (setq dist l1)
- (repeat (1- n)
- (setq dist (+ dist li))
- (setq pt (vlax-curve-getPointAtDist obj dist))
- (setq pts (cons pt pts))
- )
- )
- )
- (setq i (1+ i))
- )
- (if (= (vla-get-closed obj) :vlax-false)
- (setq pt (vlax-curve-getEndPoint obj)
- pts (cons pt pts)
- )
- )
- pts
- )
- ;;;绘制多段线
- (defun Make-Poly (pp / x)
- (entmake ;画凸包
- (append
- '((0 . "LWPOLYLINE")
- (100 . "AcDbEntity")
- (100 . "AcDbPolyline")
- )
- (list (cons 90 (length pp))) ;顶点个数
- (mapcar
- (function (lambda (x) (cons 10 x)))
- pp
- ) ;多段线顶点
- (list (cons 70 1)) ;闭合的
- (list (cons 62 1)) ;红色的
- )
- )
- )
复制代码 |
|